From c6cdba7a8d0dfce22634f88258f61abb09ecca5a Mon Sep 17 00:00:00 2001 From: Stanislav Chzhen Date: Wed, 24 Apr 2024 14:21:44 +0300 Subject: [PATCH] all: add tests --- internal/client/persistent.go | 4 +- internal/client/persistent_internal_test.go | 2 +- internal/home/clients_internal_test.go | 2 +- internal/home/clientshttp.go | 12 +- internal/home/clientshttp_internal_test.go | 281 ++++++++++++++++++++ 5 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 internal/home/clientshttp_internal_test.go diff --git a/internal/client/persistent.go b/internal/client/persistent.go index ec6259fd..06e346f4 100644 --- a/internal/client/persistent.go +++ b/internal/client/persistent.go @@ -229,9 +229,9 @@ func (c *Persistent) IDsLen() (n int) { return len(c.IPs) + len(c.Subnets) + len(c.MACs) + len(c.ClientIDs) } -// equalIDs returns true if the ids of the current and previous clients are the +// EqualIDs returns true if the ids of the current and previous clients are the // same. -func (c *Persistent) equalIDs(prev *Persistent) (equal bool) { +func (c *Persistent) EqualIDs(prev *Persistent) (equal bool) { return slices.Equal(c.IPs, prev.IPs) && slices.Equal(c.Subnets, prev.Subnets) && slices.EqualFunc(c.MACs, prev.MACs, slices.Equal[net.HardwareAddr]) && diff --git a/internal/client/persistent_internal_test.go b/internal/client/persistent_internal_test.go index 22f978ec..76da1e4b 100644 --- a/internal/client/persistent_internal_test.go +++ b/internal/client/persistent_internal_test.go @@ -118,7 +118,7 @@ func TestPersistentClient_EqualIDs(t *testing.T) { err = prev.SetIDs(tc.prevIDs) require.NoError(t, err) - tc.want(t, c.equalIDs(prev)) + tc.want(t, c.EqualIDs(prev)) }) } } diff --git a/internal/home/clients_internal_test.go b/internal/home/clients_internal_test.go index 0eb53284..d371df7b 100644 --- a/internal/home/clients_internal_test.go +++ b/internal/home/clients_internal_test.go @@ -41,7 +41,7 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) { } dhcp := &testDHCP{ - OnLeases: func() (leases []*dhcpsvc.Lease) { panic("not implemented") }, + OnLeases: func() (leases []*dhcpsvc.Lease) { return nil }, OnHostBy: func(ip netip.Addr) (host string) { return "" }, OnMACBy: func(ip netip.Addr) (mac net.HardwareAddr) { return nil }, } diff --git a/internal/home/clientshttp.go b/internal/home/clientshttp.go index 5ad08e4c..0a744f79 100644 --- a/internal/home/clientshttp.go +++ b/internal/home/clientshttp.go @@ -343,7 +343,9 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http. return } - onConfigModified() + if !clients.testing { + onConfigModified() + } } // handleDelClient is the handler for POST /control/clients/delete HTTP API. @@ -368,7 +370,9 @@ func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http. return } - onConfigModified() + if !clients.testing { + onConfigModified() + } } // updateJSON contains the name and data of the updated persistent client. @@ -425,7 +429,9 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht return } - onConfigModified() + if !clients.testing { + onConfigModified() + } } // handleFindClient is the handler for GET /control/clients/find HTTP API. diff --git a/internal/home/clientshttp_internal_test.go b/internal/home/clientshttp_internal_test.go new file mode 100644 index 00000000..9697abfe --- /dev/null +++ b/internal/home/clientshttp_internal_test.go @@ -0,0 +1,281 @@ +package home + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/AdguardTeam/AdGuardHome/internal/client" + "github.com/AdguardTeam/AdGuardHome/internal/filtering" + "github.com/AdguardTeam/AdGuardHome/internal/schedule" + "github.com/stretchr/testify/require" +) + +const ( + testClientIP1 = "1.1.1.1" + testClientIP2 = "2.2.2.2" +) + +// newPersistentClient is a helper function that returns a persistent client +// with the specified name and newly generated UID. +func newPersistentClient(name string) (c *client.Persistent) { + return &client.Persistent{ + Name: name, + UID: client.MustNewUID(), + BlockedServices: &filtering.BlockedServices{ + Schedule: &schedule.Weekly{}, + }, + } +} + +// newPersistentClientWithIDs is a helper function that returns a persistent +// client with the specified name and ids. +func newPersistentClientWithIDs(tb testing.TB, name string, ids []string) (c *client.Persistent) { + tb.Helper() + + c = newPersistentClient(name) + err := c.SetIDs(ids) + require.NoError(tb, err) + + return c +} + +// clientsCompare is a helper function that uses HTTP API to check whether want +// persistent clients are the same as the persistent clients stored in the +// clients container. +func clientsCompare(tb testing.TB, clients *clientsContainer, want []*client.Persistent) (ok bool) { + tb.Helper() + + rw := httptest.NewRecorder() + clients.handleGetClients(rw, &http.Request{}) + + body, err := io.ReadAll(rw.Body) + require.NoError(tb, err) + + clientList := &clientListJSON{} + err = json.Unmarshal(body, clientList) + require.NoError(tb, err) + + got := map[string]*client.Persistent{} + for _, cj := range clientList.Clients { + var c *client.Persistent + c, err = clients.jsonToClient(*cj, nil) + require.NoError(tb, err) + + got[c.Name] = c + } + require.Len(tb, want, len(got)) + + for _, c := range want { + var gotClient *client.Persistent + gotClient, ok = got[c.Name] + if !ok || !gotClient.EqualIDs(c) { + return false + } + } + + return true +} + +func TestClientsContainer_HandleAddClient(t *testing.T) { + clients := newClientsContainer(t) + + clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1}) + clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2}) + + clientEmptyID := newPersistentClient("empty_client_id") + clientEmptyID.ClientIDs = []string{""} + + testCases := []struct { + name string + client *client.Persistent + wantCode int + wantClient []*client.Persistent + }{{ + name: "add_one", + client: clientOne, + wantCode: http.StatusOK, + wantClient: []*client.Persistent{clientOne}, + }, { + name: "add_two", + client: clientTwo, + wantCode: http.StatusOK, + wantClient: []*client.Persistent{clientOne, clientTwo}, + }, { + name: "duplicate_client", + client: clientTwo, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientOne, clientTwo}, + }, { + name: "empty_client_id", + client: clientEmptyID, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientOne, clientTwo}, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cj := clientToJSON(tc.client) + + body, err := json.Marshal(cj) + require.NoError(t, err) + + rw := httptest.NewRecorder() + r, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(body)) + require.NoError(t, err) + + clients.handleAddClient(rw, r) + require.NoError(t, err) + require.Equal(t, tc.wantCode, rw.Code) + + ok := clientsCompare(t, clients, tc.wantClient) + require.True(t, ok) + }) + } +} + +func TestClientsContainer_HandleDelClient(t *testing.T) { + clients := newClientsContainer(t) + + clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1}) + err := clients.add(clientOne) + require.NoError(t, err) + + clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2}) + err = clients.add(clientTwo) + require.NoError(t, err) + + ok := clientsCompare(t, clients, []*client.Persistent{clientOne, clientTwo}) + require.True(t, ok) + + testCases := []struct { + name string + client *client.Persistent + wantCode int + wantClient []*client.Persistent + }{{ + name: "remove_one", + client: clientOne, + wantCode: http.StatusOK, + wantClient: []*client.Persistent{clientTwo}, + }, { + name: "duplicate_client", + client: clientOne, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientTwo}, + }, { + name: "empty_client_name", + client: newPersistentClient(""), + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientTwo}, + }, { + name: "remove_two", + client: clientTwo, + wantCode: http.StatusOK, + wantClient: []*client.Persistent{}, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cj := clientToJSON(tc.client) + + var body []byte + body, err = json.Marshal(cj) + require.NoError(t, err) + + rw := httptest.NewRecorder() + var r *http.Request + r, err = http.NewRequest(http.MethodPost, "", bytes.NewReader(body)) + require.NoError(t, err) + + clients.handleDelClient(rw, r) + require.NoError(t, err) + require.Equal(t, tc.wantCode, rw.Code) + + ok = clientsCompare(t, clients, tc.wantClient) + require.True(t, ok) + }) + } +} + +func TestClientsContainer_HandleUpdateClient(t *testing.T) { + clients := newClientsContainer(t) + + clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1}) + err := clients.add(clientOne) + require.NoError(t, err) + + ok := clientsCompare(t, clients, []*client.Persistent{clientOne}) + require.True(t, ok) + + clientModified := newPersistentClientWithIDs(t, "client2", []string{testClientIP2}) + + clientEmptyID := newPersistentClient("empty_client_id") + clientEmptyID.ClientIDs = []string{""} + + testCases := []struct { + name string + clientName string + modified *client.Persistent + wantCode int + wantClient []*client.Persistent + }{{ + name: "update_one", + clientName: clientOne.Name, + modified: clientModified, + wantCode: http.StatusOK, + wantClient: []*client.Persistent{clientModified}, + }, { + name: "empty_name", + clientName: "", + modified: clientOne, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientModified}, + }, { + name: "client_not_found", + clientName: "client_not_found", + modified: clientOne, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientModified}, + }, { + name: "empty_client_id", + clientName: clientModified.Name, + modified: clientEmptyID, + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientModified}, + }, { + name: "no_ids", + clientName: clientModified.Name, + modified: newPersistentClient("no_ids"), + wantCode: http.StatusBadRequest, + wantClient: []*client.Persistent{clientModified}, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + uj := updateJSON{ + Name: tc.clientName, + Data: *clientToJSON(tc.modified), + } + + var body []byte + body, err = json.Marshal(uj) + require.NoError(t, err) + + rw := httptest.NewRecorder() + var r *http.Request + r, err = http.NewRequest(http.MethodPost, "", bytes.NewReader(body)) + require.NoError(t, err) + + clients.handleUpdateClient(rw, r) + require.NoError(t, err) + require.Equal(t, tc.wantCode, rw.Code) + + ok = clientsCompare(t, clients, tc.wantClient) + require.True(t, ok) + }) + } +}