From 55d9612fa52b00a07397fd1963e5b46173618deb Mon Sep 17 00:00:00 2001 From: Taras <505555+ribtoks@users.noreply.github.com> Date: Wed, 7 Jan 2026 09:54:27 +0200 Subject: [PATCH] Add more portal tests (#237) * Initial plan * Initial plan for increasing test coverage in pkg/portal Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> * Add smoke tests for org.go, org_enterprise.go, property.go, settings.go and audit.go handlers Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> * Fix code review feedback: use t.Context() consistently Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> * Fix failing tests: correct paths and use admin plan for org limits Co-authored-by: ribtoks <505555+ribtoks@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .gitignore | 1 + pkg/portal/audit_test.go | 76 +++++ pkg/portal/org_test.go | 569 ++++++++++++++++++++++++++++++++++++ pkg/portal/property_test.go | 381 ++++++++++++++++++++++++ pkg/portal/settings_test.go | 175 +++++++++++ 5 files changed, 1202 insertions(+) diff --git a/.gitignore b/.gitignore index 4e455934..ea8351c4 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ fullcode.txt .DS_Store coverage_*.out +coverage_*.cov coverage_integration/ *.keys diff --git a/pkg/portal/audit_test.go b/pkg/portal/audit_test.go index fd1b4a32..67b5ea62 100644 --- a/pkg/portal/audit_test.go +++ b/pkg/portal/audit_test.go @@ -3,6 +3,8 @@ package portal import ( "context" "encoding/json" + "net/http" + "net/http/httptest" "testing" "time" @@ -10,6 +12,9 @@ import ( "github.com/PrivateCaptcha/PrivateCaptcha/pkg/common" "github.com/PrivateCaptcha/PrivateCaptcha/pkg/db" dbgen "github.com/PrivateCaptcha/PrivateCaptcha/pkg/db/generated" + db_tests "github.com/PrivateCaptcha/PrivateCaptcha/pkg/db/tests" + "github.com/PrivateCaptcha/PrivateCaptcha/pkg/email" + portal_tests "github.com/PrivateCaptcha/PrivateCaptcha/pkg/portal/tests" ) func TestUserAuditLogInitFromUser(t *testing.T) { @@ -484,3 +489,74 @@ func mustMarshalJSON(v interface{}) []byte { } return data } + +func TestGetAuditLogs(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/events", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getAuditLogs(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != auditLogsTemplate { + t.Errorf("Expected view to be %s, got %s", auditLogsTemplate, viewModel.View) + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestCreateAuditLogsContext(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + renderCtx, err := server.CreateAuditLogsContext(ctx, user, 14, 0) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if renderCtx == nil { + t.Fatal("Expected render context to be populated, got nil") + } + + if renderCtx.Days != 14 { + t.Errorf("Expected Days to be 14, got %d", renderCtx.Days) + } + + if renderCtx.AuditLogs == nil { + t.Error("Expected AuditLogs to be initialized") + } +} diff --git a/pkg/portal/org_test.go b/pkg/portal/org_test.go index 65eaf31c..32327621 100644 --- a/pkg/portal/org_test.go +++ b/pkg/portal/org_test.go @@ -187,3 +187,572 @@ func TestDeleteUserFromOrgPermissions(t *testing.T) { t.Errorf("Unexpected length of members: %v", len(members)) } } + +func TestGetNewOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/org/new", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getNewOrg(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgWizardTemplate { + t.Errorf("Expected view to be %s, got %s", orgWizardTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgWizardRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgWizardRenderContext, got %T", viewModel.Model) + } + + if len(renderCtx.Token) == 0 { + t.Error("Expected CSRF token to be populated") + } +} + +func TestGetOrgDashboard(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/tab/dashboard", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getOrgDashboard(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgDashboardTemplate { + t.Errorf("Expected view to be %s, got %s", orgDashboardTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgPropertiesRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgPropertiesRenderContext, got %T", viewModel.Model) + } + + if renderCtx.CurrentOrg == nil { + t.Fatal("Expected CurrentOrg to be populated, got nil") + } + + if renderCtx.CurrentOrg.Name != org.Name { + t.Errorf("Expected org name to be %s, got %s", org.Name, renderCtx.CurrentOrg.Name) + } +} + +func TestGetOrgProperties(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + if _, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org); err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/tab/properties", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getOrgProperties(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgPropertiesTemplate { + t.Errorf("Expected view to be %s, got %s", orgPropertiesTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgPropertiesRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgPropertiesRenderContext, got %T", viewModel.Model) + } + + if len(renderCtx.Properties) != 1 { + t.Errorf("Expected 1 property, got %d", len(renderCtx.Properties)) + } +} + +func TestGetOrgMembers(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/tab/members", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getOrgMembers(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgMembersTemplate { + t.Errorf("Expected view to be %s, got %s", orgMembersTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgMemberRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgMemberRenderContext, got %T", viewModel.Model) + } + + if !renderCtx.CanEdit { + t.Error("Expected CanEdit to be true for org owner") + } +} + +func TestGetOrgSettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/tab/settings", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getOrgSettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgSettingsTemplate { + t.Errorf("Expected view to be %s, got %s", orgSettingsTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgSettingsRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgSettingsRenderContext, got %T", viewModel.Model) + } + + if !renderCtx.CanEdit { + t.Error("Expected CanEdit to be true for org owner") + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestGetOrgAuditLogs(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/tab/events", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getOrgAuditLogs(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgAuditLogsTemplate { + t.Errorf("Expected view to be %s, got %s", orgAuditLogsTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgAuditLogsRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgAuditLogsRenderContext, got %T", viewModel.Model) + } + + if !renderCtx.CanView { + t.Error("Expected CanView to be true for org owner") + } +} + +func TestPutOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + newName := t.Name() + "-updated" + form := url.Values{} + form.Set(common.ParamCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + form.Set(common.ParamName, newName) + + req := httptest.NewRequest("PUT", fmt.Sprintf("/org/%s/edit", server.IDHasher.Encrypt(int(org.ID))), strings.NewReader(form.Encode())) + req.AddCookie(cookie) + req.Header.Set(common.HeaderContentType, common.ContentTypeURLEncoded) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.putOrg(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != orgSettingsTemplate { + t.Errorf("Expected view to be %s, got %s", orgSettingsTemplate, viewModel.View) + } + + renderCtx, ok := viewModel.Model.(*orgSettingsRenderContext) + if !ok { + t.Fatalf("Expected Model to be *orgSettingsRenderContext, got %T", viewModel.Model) + } + + if renderCtx.CurrentOrg.Name != newName { + t.Errorf("Expected org name to be %s, got %s", newName, renderCtx.CurrentOrg.Name) + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestPostNewOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + // Use admin plan with higher org limits to allow creating additional orgs + adminPlan := server.PlanService.GetInternalAdminPlan() + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), adminPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + orgName := t.Name() + "-new-org" + form := url.Values{} + form.Set(common.ParamCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + form.Set(common.ParamName, orgName) + + req := httptest.NewRequest("POST", "/org/new", strings.NewReader(form.Encode())) + req.AddCookie(cookie) + req.Header.Set(common.HeaderContentType, common.ContentTypeURLEncoded) + + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + resp := w.Result() + if resp.StatusCode != http.StatusSeeOther { + t.Errorf("Unexpected status code %v", resp.StatusCode) + } + + location, err := resp.Location() + if err != nil { + t.Fatalf("Expected redirect response but got error: %v", err) + } + + if !strings.HasPrefix(location.String(), "/org/") { + t.Errorf("Unexpected redirect path: %s", location.String()) + } +} + +func TestJoinOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + owner, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name()+"_1", testPlan) + if err != nil { + t.Fatalf("Failed to create owner account: %v", err) + } + + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name()+"_2", testPlan) + if err != nil { + t.Fatalf("Failed to create user account: %v", err) + } + + if _, err := store.Impl().InviteUserToOrg(ctx, owner, org, user); err != nil { + t.Fatal(err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("PUT", fmt.Sprintf("/org/%s/members", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.Header.Set(common.HeaderCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + resp := w.Result() + if resp.StatusCode != http.StatusSeeOther { + t.Errorf("Unexpected status code %v", resp.StatusCode) + } + + members, err := store.Impl().RetrieveOrganizationUsers(ctx, org.ID) + if err != nil { + t.Fatal(err) + } + + hasUser := false + for _, m := range members { + if m.User.ID == user.ID && m.Level == dbgen.AccessLevelMember { + hasUser = true + break + } + } + + if !hasUser { + t.Error("User should be a member of the org after joining") + } +} + +func TestLeaveOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + owner, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name()+"_1", testPlan) + if err != nil { + t.Fatalf("Failed to create owner account: %v", err) + } + + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name()+"_2", testPlan) + if err != nil { + t.Fatalf("Failed to create user account: %v", err) + } + + if _, err := store.Impl().InviteUserToOrg(ctx, owner, org, user); err != nil { + t.Fatal(err) + } + + if _, err := store.Impl().JoinOrg(ctx, org.ID, user); err != nil { + t.Fatal(err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("DELETE", fmt.Sprintf("/org/%s/members", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.Header.Set(common.HeaderCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + resp := w.Result() + if resp.StatusCode != http.StatusSeeOther { + t.Errorf("Unexpected status code %v", resp.StatusCode) + } + + members, err := store.Impl().RetrieveOrganizationUsers(ctx, org.ID) + if err != nil { + t.Fatal(err) + } + + for _, m := range members { + if m.User.ID == user.ID && m.Level == dbgen.AccessLevelMember { + t.Error("User should have left the org (level should change from member to invited)") + } + } +} + +func TestDeleteOrg(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + // Use admin plan to allow creating extra org + adminPlan := server.PlanService.GetInternalAdminPlan() + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), adminPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + org, _, err := store.Impl().CreateNewOrganization(ctx, t.Name()+"-delete-org", user.ID) + if err != nil { + t.Fatalf("Failed to create extra org: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("DELETE", fmt.Sprintf("/org/%s/delete", server.IDHasher.Encrypt(int(org.ID))), nil) + req.AddCookie(cookie) + req.Header.Set(common.HeaderCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + resp := w.Result() + if resp.StatusCode != http.StatusSeeOther { + t.Errorf("Unexpected status code %v", resp.StatusCode) + } + + orgs, err := store.Impl().RetrieveUserOrganizations(ctx, user.ID) + if err != nil { + t.Fatal(err) + } + + for _, o := range orgs { + if o.Organization.ID == org.ID { + t.Error("Org should have been deleted") + } + } +} diff --git a/pkg/portal/property_test.go b/pkg/portal/property_test.go index ee9a45c6..34547442 100644 --- a/pkg/portal/property_test.go +++ b/pkg/portal/property_test.go @@ -413,3 +413,384 @@ func TestGetPropertyStats(t *testing.T) { t.Errorf("Expected 2 total verified, got %d", totalVerified) } } + +func TestGetOrgProperty(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + renderCtx, dbProperty, err := server.getOrgProperty(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if renderCtx == nil { + t.Fatal("Expected render context to be populated, got nil") + } + + if dbProperty == nil { + t.Fatal("Expected property to be populated, got nil") + } + + if dbProperty.ID != property.ID { + t.Errorf("Expected property ID to be %d, got %d", property.ID, dbProperty.ID) + } + + if renderCtx.Property == nil { + t.Fatal("Expected Property in render context to be populated, got nil") + } + + if !renderCtx.CanEdit { + t.Error("Expected CanEdit to be true for property creator") + } +} + +func TestGetOrgPropertySettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s/tab/settings", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + renderCtx, auditEvent, err := server.getOrgPropertySettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if renderCtx == nil { + t.Fatal("Expected render context to be populated, got nil") + } + + if renderCtx.Tab != propertySettingsTabIndex { + t.Errorf("Expected tab to be %d, got %d", propertySettingsTabIndex, renderCtx.Tab) + } + + if auditEvent == nil { + t.Error("Expected audit event to be populated") + } +} + +func TestGetPropertyDashboard(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getPropertyDashboard(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != propertyDashboardTemplate { + t.Errorf("Expected view to be %s, got %s", propertyDashboardTemplate, viewModel.View) + } +} + +func TestGetPropertyReportsTab(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s/tab/reports", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getPropertyReportsTab(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != propertyDashboardReportsTemplate { + t.Errorf("Expected view to be %s, got %s", propertyDashboardReportsTemplate, viewModel.View) + } +} + +func TestGetPropertySettingsTab(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s/tab/settings", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getPropertySettingsTab(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != propertyDashboardSettingsTemplate { + t.Errorf("Expected view to be %s, got %s", propertyDashboardSettingsTemplate, viewModel.View) + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestGetPropertyIntegrationsTab(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s/tab/integrations", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getPropertyIntegrationsTab(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != propertyDashboardIntegrationsTemplate { + t.Errorf("Expected view to be %s, got %s", propertyDashboardIntegrationsTemplate, viewModel.View) + } +} + +func TestGetPropertyAuditLogsTab(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", fmt.Sprintf("/org/%s/property/%s/tab/events", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.SetPathValue(common.ParamOrg, server.IDHasher.Encrypt(int(org.ID))) + req.SetPathValue(common.ParamProperty, server.IDHasher.Encrypt(int(property.ID))) + + w := httptest.NewRecorder() + + viewModel, err := server.getPropertyAuditLogsTab(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if viewModel.View != propertyDashboardAuditLogsTemplate { + t.Errorf("Expected view to be %s, got %s", propertyDashboardAuditLogsTemplate, viewModel.View) + } +} + +func TestDeleteProperty(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := t.Context() + user, org, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + property, _, err := server.Store.Impl().CreateNewProperty(ctx, db_tests.CreateNewPropertyParams(user.ID, "example.com"), org) + if err != nil { + t.Fatalf("Failed to create new property: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("DELETE", fmt.Sprintf("/org/%s/property/%s/delete", server.IDHasher.Encrypt(int(org.ID)), server.IDHasher.Encrypt(int(property.ID))), nil) + req.AddCookie(cookie) + req.Header.Set(common.HeaderCSRFToken, server.XSRF.Token(strconv.Itoa(int(user.ID)))) + + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + resp := w.Result() + if resp.StatusCode != http.StatusSeeOther { + t.Errorf("Unexpected status code %v", resp.StatusCode) + } + + properties, _, err := store.Impl().RetrieveOrgProperties(ctx, org, 0, db.MaxOrgPropertiesPageSize) + if err != nil { + t.Fatal(err) + } + + if len(properties) != 0 { + t.Error("Property should have been deleted") + } +} diff --git a/pkg/portal/settings_test.go b/pkg/portal/settings_test.go index 292f3bf1..0599471e 100644 --- a/pkg/portal/settings_test.go +++ b/pkg/portal/settings_test.go @@ -181,3 +181,178 @@ func TestRotateAPIKey(t *testing.T) { t.Error("Key external ID was not rotated") } } + +func TestGetSettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := common.TraceContext(t.Context(), t.Name()) + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/settings", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getSettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + if !strings.HasSuffix(viewModel.View, "page.html") { + t.Errorf("Expected view to end with page.html, got %s", viewModel.View) + } +} + +func TestGetGeneralSettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := common.TraceContext(t.Context(), t.Name()) + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/settings/tab/general", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getGeneralSettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + renderCtx, ok := viewModel.Model.(*settingsGeneralRenderContext) + if !ok { + t.Fatalf("Expected Model to be *settingsGeneralRenderContext, got %T", viewModel.Model) + } + + if renderCtx.Name != user.Name { + t.Errorf("Expected Name to be %s, got %s", user.Name, renderCtx.Name) + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestGetAPIKeysSettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := common.TraceContext(t.Context(), t.Name()) + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/settings/tab/apikeys", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getAPIKeysSettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + renderCtx, ok := viewModel.Model.(*settingsAPIKeysRenderContext) + if !ok { + t.Fatalf("Expected Model to be *settingsAPIKeysRenderContext, got %T", viewModel.Model) + } + + if renderCtx.Keys == nil { + t.Error("Expected Keys to be initialized (even if empty)") + } + + if viewModel.AuditEvent == nil { + t.Error("Expected AuditEvent to be populated") + } +} + +func TestGetUsageSettings(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := common.TraceContext(t.Context(), t.Name()) + user, _, err := db_tests.CreateNewAccountForTest(ctx, store, t.Name(), testPlan) + if err != nil { + t.Fatalf("Failed to create account: %v", err) + } + + srv := http.NewServeMux() + server.Setup(portalDomain(), common.NoopMiddleware).Register(srv) + + cookie, err := portal_tests.AuthenticateSuite(ctx, user.Email, srv, server.XSRF, server.Sessions.CookieName, server.Mailer.(*email.StubMailer)) + if err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/settings/tab/usage", nil) + req.AddCookie(cookie) + + w := httptest.NewRecorder() + + viewModel, err := server.getUsageSettings(w, req) + if err != nil { + t.Fatalf("Expected no error, got: %v", err) + } + + if viewModel == nil { + t.Fatal("Expected ViewModel to be populated, got nil") + } + + renderCtx, ok := viewModel.Model.(*settingsUsageRenderContext) + if !ok { + t.Fatalf("Expected Model to be *settingsUsageRenderContext, got %T", viewModel.Model) + } + + if renderCtx.OrgsCount == 0 { + t.Error("Expected OrgsCount to be at least 1") + } +}