diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2bd10683..256947d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build +name: build on: [push, pull_request] diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bfcda9cc..26f5b000 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,11 +11,8 @@ jobs: - name: check out code uses: actions/checkout@v2 - - name: download mod - run: go mod download - - - name: run functional tests - run: ./scripts/functional-tests.sh + - name: run mqtt tests + run: ./scripts/mqtt-tests.sh - name: run auth tests - run: go test -v ./auth + run: go test -v ./auth \ No newline at end of file diff --git a/README.md b/README.md index 8d26b28c..6285e467 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,10 @@ See [API](./API.md) ## Functional tests -This type of tests are executed all locally and you need to configure a dedicated docker container: -- `docker pull guerra1994/go-mqtt-docker-env` -- follow the steps [here](https://hub.docker.com/r/guerra1994/go-mqtt-docker-env) to run it -- run mosquitto broker in background mode using `mosquitto > /dev/null 2>&1 &` +These tests can be executed locally. To do that, you need to configure a dedicated docker container: +- get the image `docker pull guerra1994/go-docker-mqtt-ubuntu-env` +- enter the container `docker run -it -v $(pwd):/home --privileged --name gmd guerra1994/go-docker-mqtt-ubuntu-env` +- run the mosquitto MQTT broker in background mode `mosquitto > /dev/null 2>&1 &` - then run your test, for example `go test -v --tags=functional --run="TestDockerPsApi"` diff --git a/handlers_apt_repositories.go b/handlers_apt_repositories.go index a22e1891..18a0abe7 100644 --- a/handlers_apt_repositories.go +++ b/handlers_apt_repositories.go @@ -1,7 +1,7 @@ // // This file is part of arduino-connector // -// Copyright (C) 2017-2018 Arduino AG (http://www.arduino.cc/) +// Copyright (C) 2017-2020 Arduino AG (http://www.arduino.cc/) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,25 +28,19 @@ import ( // AptRepositoryListEvent sends a list of available repositories func (s *Status) AptRepositoryListEvent(client mqtt.Client, msg mqtt.Message) { - // Get packages from system all, err := apt.ParseAPTConfigFolder("/etc/apt") if err != nil { s.Error("/apt/repos/list", fmt.Errorf("Retrieving repositories: %s", err)) return } - // Send result data, err := json.Marshal(all) if err != nil { s.Error("/apt/repos/list", fmt.Errorf("Json marshal result: %s", err)) return } - //var out bytes.Buffer - //json.Indent(&out, data, "", " ") - //fmt.Println(string(out.Bytes())) - - s.Info("/apt/repos/list", string(data)) + s.SendInfo(s.topicPertinence+"/apt/repos/list", string(data)) } // AptRepositoryAddEvent adds a repository to the apt configuration @@ -66,7 +60,7 @@ func (s *Status) AptRepositoryAddEvent(client mqtt.Client, msg mqtt.Message) { return } - s.Info("/apt/repos/add", "OK") + s.SendInfo(s.topicPertinence+"/apt/repos/add", "OK") } // AptRepositoryRemoveEvent removes a repository from the apt configuration @@ -86,7 +80,7 @@ func (s *Status) AptRepositoryRemoveEvent(client mqtt.Client, msg mqtt.Message) return } - s.Info("/apt/repos/remove", "OK") + s.SendInfo(s.topicPertinence+"/apt/repos/remove", "OK") } // AptRepositoryEditEvent modifies a repository definition in the apt configuration @@ -107,5 +101,5 @@ func (s *Status) AptRepositoryEditEvent(client mqtt.Client, msg mqtt.Message) { return } - s.Info("/apt/repos/edit", "OK") + s.SendInfo(s.topicPertinence+"/apt/repos/edit", "OK") } diff --git a/handlers_apt_repositories_test.go b/handlers_apt_repositories_test.go new file mode 100644 index 00000000..ba0cb462 --- /dev/null +++ b/handlers_apt_repositories_test.go @@ -0,0 +1,253 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "strings" + "testing" + "time" + + apt "github.com/arduino/go-apt-client" + mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/stretchr/testify/assert" +) + +func TestAptList(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/list/post", s, s.AptRepositoryListEvent, false) + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/list", "{}", 1*time.Second) + + assert.NotEmpty(t, resp) +} + +func TestAptAddError(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/add/post", s, s.AptRepositoryAddEvent, true) + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/add", "{test}", 1*time.Second) + + assert.True(t, strings.HasPrefix(resp, "ERROR")) + assert.True(t, strings.Contains(resp, "Unmarshal")) +} + +func TestAptAdd(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/add/post", s, s.AptRepositoryAddEvent, true) + + var params struct { + Repository *apt.Repository `json:"repository"` + } + + params.Repository = &apt.Repository{ + Enabled: false, + SourceRepo: true, + URI: "http://ppa.launchpad.net/test/ubuntu", + Distribution: "zesty", + Components: "main", + Comment: "", + } + + data, err := json.Marshal(params) + if err != nil { + t.Error(err) + } + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/add", string(data), 1*time.Second) + assert.Equal(t, "INFO: OK\n", resp) + + defer func() { + err = apt.RemoveRepository(params.Repository, "/etc/apt") + if err != nil { + t.Error(err) + } + }() + + all, err := apt.ParseAPTConfigFolder("/etc/apt") + if err != nil { + s.Error("/apt/repos/list", fmt.Errorf("Retrieving repositories: %s", err)) + return + } + + assert.True(t, all.Contains(params.Repository)) +} + +func TestAptRemoveError(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/remove/post", s, s.AptRepositoryRemoveEvent, true) + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/remove", "{test}", 1*time.Second) + + assert.True(t, strings.HasPrefix(resp, "ERROR")) + assert.True(t, strings.Contains(resp, "Unmarshal")) +} + +func TestAptRemove(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/remove/post", s, s.AptRepositoryRemoveEvent, true) + + var params struct { + Repository *apt.Repository `json:"repository"` + } + + params.Repository = &apt.Repository{ + Enabled: false, + SourceRepo: true, + URI: "http://ppa.launchpad.net/test/ubuntu", + Distribution: "zesty", + Components: "main", + Comment: "", + } + + errAdd := apt.AddRepository(params.Repository, "/etc/apt") + if errAdd != nil { + t.Error(errAdd) + } + + data, err := json.Marshal(params) + if err != nil { + t.Error(err) + } + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/remove", string(data), 1*time.Second) + assert.Equal(t, "INFO: OK\n", resp) + + all, err := apt.ParseAPTConfigFolder("/etc/apt") + if err != nil { + s.Error("/apt/repos/list", fmt.Errorf("Retrieving repositories: %s", err)) + return + } + + assert.False(t, all.Contains(params.Repository)) +} + +func TestAptEditError(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/edit/post", s, s.AptRepositoryEditEvent, true) + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/edit", "{test}", 1*time.Second) + + assert.True(t, strings.HasPrefix(resp, "ERROR")) + assert.True(t, strings.Contains(resp, "Unmarshal")) +} + +func TestAptEdit(t *testing.T) { + ui := NewMqttTestClientLocal() + defer ui.Close() + + s := NewStatus(program{}.Config, nil, nil, "") + s.mqttClient = mqtt.NewClient(mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")) + if token := s.mqttClient.Connect(); token.Wait() && token.Error() != nil { + log.Fatal(token.Error()) + } + defer s.mqttClient.Disconnect(100) + + subscribeTopic(s.mqttClient, "0", "/apt/repos/edit/post", s, s.AptRepositoryEditEvent, true) + + var params struct { + OldRepository *apt.Repository `json:"old_repository"` + NewRepository *apt.Repository `json:"new_repository"` + } + + params.OldRepository = &apt.Repository{ + Enabled: false, + SourceRepo: true, + URI: "http://ppa.launchpad.net/OldTest/ubuntu", + Distribution: "zesty", + Components: "main", + Comment: "old", + } + + params.NewRepository = &apt.Repository{ + Enabled: false, + SourceRepo: true, + URI: "http://ppa.launchpad.net/NewTest/ubuntu", + Distribution: "zesty", + Components: "main", + Comment: "new", + } + + errAdd := apt.AddRepository(params.OldRepository, "/etc/apt") + if errAdd != nil { + t.Error(errAdd) + } + + defer func() { + err := apt.RemoveRepository(params.NewRepository, "/etc/apt") + if err != nil { + t.Error(err) + } + }() + + data, err := json.Marshal(params) + if err != nil { + t.Error(err) + } + + resp := ui.MqttSendAndReceiveTimeout(t, "/apt/repos/edit", string(data), 1*time.Second) + assert.Equal(t, "INFO: OK\n", resp) + + all, err := apt.ParseAPTConfigFolder("/etc/apt") + if err != nil { + s.Error("/apt/repos/list", fmt.Errorf("Retrieving repositories: %s", err)) + return + } + + assert.True(t, all.Contains(params.NewRepository)) + assert.False(t, all.Contains(params.OldRepository)) +} diff --git a/handlers_containers_functional_test.go b/handlers_containers_functional_test.go index 7047e0a5..2a53fd06 100644 --- a/handlers_containers_functional_test.go +++ b/handlers_containers_functional_test.go @@ -23,20 +23,6 @@ import ( "golang.org/x/net/context" ) -// NewMqttTestClientLocal creates mqtt client in localhost:1883 -func NewMqttTestClientLocal() *MqttTestClient { - uiOptions := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("UI") - ui := mqtt.NewClient(uiOptions) - if token := ui.Connect(); token.Wait() && token.Error() != nil { - panic(token.Error()) - } - - return &MqttTestClient{ - ui, - "", - } -} - type testStatus struct { appStatus *Status ui *MqttTestClient diff --git a/scripts/functional-tests.sh b/scripts/mqtt-tests.sh similarity index 59% rename from scripts/functional-tests.sh rename to scripts/mqtt-tests.sh index 08b796ce..40ddab09 100755 --- a/scripts/functional-tests.sh +++ b/scripts/mqtt-tests.sh @@ -5,4 +5,5 @@ set -euo pipefail trap 'kill "$(pidof mosquitto)"' EXIT mosquitto > /dev/null & -go test -v --tags=functional --run="TestDocker" \ No newline at end of file +go test -v --tags=functional --run=TestDocker +go test -v --run=TestApt \ No newline at end of file diff --git a/test/go-mosquitto-ubuntu-env b/test/go-mosquitto-ubuntu-env new file mode 100644 index 00000000..9699775d --- /dev/null +++ b/test/go-mosquitto-ubuntu-env @@ -0,0 +1,16 @@ +FROM ubuntu +LABEL maintainer="guerinoni.federico@gmail.com" + +RUN apt-get update +RUN apt-get -y install \ + curl \ + gcc \ + mosquitto + +RUN curl -O https://dl.google.com/go/go1.15.linux-amd64.tar.gz +RUN tar xvf go1.15.linux-amd64.tar.gz +RUN mv go /usr/local + +ENV PATH=$PATH:/usr/local/go/bin + +WORKDIR /home \ No newline at end of file diff --git a/tests_helper.go b/tests_helper.go index 41d25c4d..4317225f 100644 --- a/tests_helper.go +++ b/tests_helper.go @@ -68,6 +68,20 @@ func NewMqttTestClient() *MqttTestClient { } } +// NewMqttTestClientLocal creates mqtt client in localhost:1883 +func NewMqttTestClientLocal() *MqttTestClient { + uiOptions := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("UI") + ui := mqtt.NewClient(uiOptions) + if token := ui.Connect(); token.Wait() && token.Error() != nil { + panic(token.Error()) + } + + return &MqttTestClient{ + ui, + "", + } +} + // Close disconnect client func (tmc *MqttTestClient) Close() { tmc.client.Disconnect(100)