From fd041d9b3c218947c9ea170674df08a885ac1e0b Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Thu, 3 Aug 2023 16:35:06 +1000 Subject: [PATCH] Add more unit tests --- backend/internal/entity/host/entity_test.go | 220 ++++++++++++++++++++ backend/internal/entity/host/model.go | 15 +- 2 files changed, 228 insertions(+), 7 deletions(-) create mode 100644 backend/internal/entity/host/entity_test.go diff --git a/backend/internal/entity/host/entity_test.go b/backend/internal/entity/host/entity_test.go new file mode 100644 index 0000000..569c308 --- /dev/null +++ b/backend/internal/entity/host/entity_test.go @@ -0,0 +1,220 @@ +package host + +import ( + "regexp" + "testing" + "time" + + "npm/internal/model" + "npm/internal/status" + "npm/internal/test" + "npm/internal/types" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +// +------------+ +// | Setup | +// +------------+ + +type testsuite struct { + suite.Suite + mock sqlmock.Sqlmock + singleRow *sqlmock.Rows +} + +func makeJSONB(str string) types.JSONB { + m := types.JSONB{} + m.UnmarshalJSON([]byte(str)) + return m +} + +// SetupTest is executed before each test +func (s *testsuite) SetupTest() { + var err error + s.mock, err = test.Setup() + require.NoError(s.T(), err) + + // These rows need to be intantiated for each test as they are + // read in the db object, and their row position is not resettable + // between tests. + s.singleRow = sqlmock.NewRows([]string{ + "id", + "user_id", + "type", + "nginx_template_id", + "listen_interface", + "domain_names", + "upstream_id", + "proxy_scheme", + "proxy_host", + "proxy_port", + "certificate_id", + "access_list_id", + "ssl_forced", + "caching_enabled", + "block_exploits", + "allow_websocket_upgrade", + "http2_support", + "hsts_enabled", + "hsts_subdomains", + "paths", + "advanced_config", + "status", + "error_message", + "is_disabled", + }).AddRow( + 10, // ID + 100, // UserID + "proxy", // Type + 20, // NginxTemplateID + "", // ListenInterface + makeJSONB("[\"example.com\"]"), // DomainNames + 0, // UpstreamID + "http", // ProxyScheme + "127.0.0.1", // ProxyHost + 3000, // ProxyPort + types.NullableDBUint{Uint: 0}, // CertificateID + types.NullableDBUint{Uint: 0}, // AccessListID + false, // SSLForced + false, // CachingEnabled + false, // BlockExploits + false, // AllowWebsocketUpgrade + false, // HTTP2Support + false, // HSTSEnabled + false, // HSTSSubdomains + "", // Paths + "", // AdvancedConfig + status.StatusReady, // Status + "", // ErrorMessage + false, // IsDisabled + ) +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestExampleTestSuite(t *testing.T) { + suite.Run(t, new(testsuite)) +} + +// +------------+ +// | Tests | +// +------------+ + +func (s *testsuite) TestGetByID() { + s.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "host" WHERE "host"."id" = $1 AND "host"."is_deleted" = $2 ORDER BY "host"."id" LIMIT 1`)). + WithArgs(10, 0). + WillReturnRows(s.singleRow) + + m, err := GetByID(10) + require.NoError(s.T(), err) + require.NoError(s.T(), s.mock.ExpectationsWereMet()) + assert.Equal(s.T(), uint(10), m.ID) +} + +func (s *testsuite) TestSave() { + s.mock.ExpectBegin() + s.mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO "host" ("created_at","updated_at","is_deleted","user_id","type","nginx_template_id","listen_interface","domain_names","upstream_id","proxy_scheme","proxy_host","proxy_port","certificate_id","access_list_id","ssl_forced","caching_enabled","block_exploits","allow_websocket_upgrade","http2_support","hsts_enabled","hsts_subdomains","paths","advanced_config","status","error_message","is_disabled") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26) RETURNING "id"`)). + WithArgs( + sqlmock.AnyArg(), + sqlmock.AnyArg(), + 0, + 100, + "proxy", + 20, + "", + "[\"example.com\"]", + nil, + "http", + "127.0.0.1", + 3000, // proxy_port + nil, + nil, + false, + false, + false, + false, + false, + false, + false, + "", + "", + status.StatusReady, + "", + false, + ). + WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow("11")) + s.mock.ExpectCommit() + + // New model, as system + m := Model{ + UserID: 100, + Type: "proxy", + NginxTemplateID: 20, + DomainNames: makeJSONB("[\"example.com\"]"), + ProxyScheme: "http", + ProxyHost: "127.0.0.1", + ProxyPort: 3000, + Status: status.StatusReady, + } + err := m.Save(true) + require.NoError(s.T(), err) + require.NoError(s.T(), s.mock.ExpectationsWereMet()) +} + +func (s *testsuite) TestDelete() { + s.mock.ExpectBegin() + s.mock. + ExpectExec(regexp.QuoteMeta(`UPDATE "host" SET "is_deleted"=$1 WHERE "host"."id" = $2 AND "host"."is_deleted" = $3`)). + WithArgs(1, 10, 0). + WillReturnResult(sqlmock.NewResult(0, 1)) + s.mock.ExpectCommit() + + m := Model{} + err := m.Delete() + assert.Equal(s.T(), "Unable to delete a new object", err.Error()) + + m2 := Model{ + ModelBase: model.ModelBase{ + ID: 10, + }, + } + err2 := m2.Delete() + require.NoError(s.T(), err2) + require.NoError(s.T(), s.mock.ExpectationsWereMet()) +} + +func (s *testsuite) TestGetTemplate() { + m := Model{ + ModelBase: model.ModelBase{ + ID: 10, + CreatedAt: time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(), + UpdatedAt: time.Date(2018, 8, 12, 7, 30, 24, 16, time.UTC).UnixMilli(), + }, + UserID: 100, + Type: "proxy", + NginxTemplateID: 20, + DomainNames: makeJSONB("[\"example.com\"]"), + ProxyScheme: "http", + ProxyHost: "127.0.0.1", + ProxyPort: 3000, + Status: status.StatusReady, + } + + t := m.GetTemplate() + assert.Equal(s.T(), uint(10), t.ID) + assert.Equal(s.T(), "Mon, 01 Jan 2018 10:00:00 AEST", t.CreatedAt) + assert.Equal(s.T(), "Sun, 12 Aug 2018 17:30:24 AEST", t.UpdatedAt) + assert.Equal(s.T(), uint(100), t.UserID) + assert.Equal(s.T(), "proxy", t.Type) + assert.Equal(s.T(), uint(20), t.NginxTemplateID) + assert.Equal(s.T(), "http", t.ProxyScheme) + assert.Equal(s.T(), "127.0.0.1", t.ProxyHost) + assert.Equal(s.T(), 3000, t.ProxyPort) + assert.Equal(s.T(), []string{"example.com"}, t.DomainNames) + assert.Equal(s.T(), status.StatusReady, t.Status) +} diff --git a/backend/internal/entity/host/model.go b/backend/internal/entity/host/model.go index 64d0d11..c517219 100644 --- a/backend/internal/entity/host/model.go +++ b/backend/internal/entity/host/model.go @@ -1,7 +1,8 @@ package host import ( - "fmt" + "time" + "npm/internal/database" "npm/internal/entity/certificate" "npm/internal/entity/nginxtemplate" @@ -85,15 +86,15 @@ func (m *Model) Save(skipConfiguration bool) error { return result.Error } -// Delete will mark row as deleted -func (m *Model) Delete() bool { +// Delete will mark a row as deleted +func (m *Model) Delete() error { if m.ID == 0 { // Can't delete a new object - return false + return eris.New("Unable to delete a new object") } db := database.GetDB() result := db.Delete(m) - return result.Error == nil + return result.Error } // Expand will fill in more properties @@ -140,8 +141,8 @@ func (m *Model) GetTemplate() Template { t := Template{ ID: m.ID, - CreatedAt: fmt.Sprintf("%d", m.CreatedAt), // todo: format as nice string - UpdatedAt: fmt.Sprintf("%d", m.UpdatedAt), // todo: format as nice string + CreatedAt: time.UnixMilli(m.CreatedAt).Format(time.RFC1123), + UpdatedAt: time.UnixMilli(m.UpdatedAt).Format(time.RFC1123), UserID: m.UserID, Type: m.Type, NginxTemplateID: m.NginxTemplateID,