diff options
Diffstat (limited to 'internal/commands/archive_test.go')
-rw-r--r-- | internal/commands/archive_test.go | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/internal/commands/archive_test.go b/internal/commands/archive_test.go new file mode 100644 index 0000000..ae9ce1f --- /dev/null +++ b/internal/commands/archive_test.go @@ -0,0 +1,517 @@ +package commands + +import ( + "context" + "strings" + "testing" + + "git.tjp.lol/punchcard/internal/queries" +) + +func TestArchiveCommand(t *testing.T) { + tests := []struct { + name string + setupData func(*queries.Queries) (clientID, projectID int64) + args []string + expectedOutputs []string + expectError bool + errorContains string + }{ + { + name: "archive client by name", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + return client.ID, 0 + }, + args: []string{"archive", "--client", "TestClient"}, + expectedOutputs: []string{"Archived client: TestClient"}, + expectError: false, + }, + { + name: "archive client by ID", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ClientByID", + }) + return client.ID, 0 + }, + args: []string{"archive", "--client", "1"}, + expectedOutputs: []string{"Archived client: ClientByID"}, + expectError: false, + }, + { + name: "archive project by name", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "TestProject", + ClientID: client.ID, + }) + return client.ID, project.ID + }, + args: []string{"archive", "--project", "TestProject"}, + expectedOutputs: []string{"Archived project: TestProject"}, + expectError: false, + }, + { + name: "archive project by ID", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ProjectByID", + ClientID: client.ID, + }) + return client.ID, project.ID + }, + args: []string{"archive", "--project", "1"}, + expectedOutputs: []string{"Archived project: ProjectByID"}, + expectError: false, + }, + { + name: "archive without flags returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"archive"}, + expectError: true, + errorContains: "either --client or --project must be specified", + }, + { + name: "archive with both client and project flags returns error", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "TestProject", + ClientID: client.ID, + }) + return client.ID, project.ID + }, + args: []string{"archive", "--client", "TestClient", "--project", "TestProject"}, + expectError: true, + errorContains: "cannot specify both --client and --project", + }, + { + name: "archive nonexistent client returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"archive", "--client", "NonexistentClient"}, + expectError: true, + errorContains: "failed to find client", + }, + { + name: "archive nonexistent project returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"archive", "--project", "NonexistentProject"}, + expectError: true, + errorContains: "failed to find project", + }, + { + name: "archive already archived client succeeds", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "AlreadyArchived", + }) + _ = q.ArchiveClient(context.Background(), client.ID) + return client.ID, 0 + }, + args: []string{"archive", "--client", "AlreadyArchived"}, + expectedOutputs: []string{"Archived client: AlreadyArchived"}, + expectError: false, + }, + { + name: "archive client using short flag", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ShortFlagClient", + }) + return client.ID, 0 + }, + args: []string{"archive", "-c", "ShortFlagClient"}, + expectedOutputs: []string{"Archived client: ShortFlagClient"}, + expectError: false, + }, + { + name: "archive project using short flag", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ShortFlagProject", + ClientID: client.ID, + }) + return client.ID, project.ID + }, + args: []string{"archive", "-p", "ShortFlagProject"}, + expectedOutputs: []string{"Archived project: ShortFlagProject"}, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q, cleanup := setupTestDB(t) + defer cleanup() + + tt.setupData(q) + + output, err := executeCommandWithDB(t, q, tt.args...) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error but got none") + } else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error()) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + found := false + for _, expectedOutput := range tt.expectedOutputs { + if strings.Contains(output, expectedOutput) { + found = true + break + } + } + + if !found { + t.Errorf("Expected output to contain one of %v, got %q", tt.expectedOutputs, output) + } + }) + } +} + +func TestUnarchiveCommand(t *testing.T) { + tests := []struct { + name string + setupData func(*queries.Queries) (clientID, projectID int64) + args []string + expectedOutputs []string + expectError bool + errorContains string + }{ + { + name: "unarchive client by name", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ArchivedClient", + }) + _ = q.ArchiveClient(context.Background(), client.ID) + return client.ID, 0 + }, + args: []string{"unarchive", "--client", "ArchivedClient"}, + expectedOutputs: []string{"Unarchived client: ArchivedClient"}, + expectError: false, + }, + { + name: "unarchive client by ID", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ClientByID", + }) + _ = q.ArchiveClient(context.Background(), client.ID) + return client.ID, 0 + }, + args: []string{"unarchive", "--client", "1"}, + expectedOutputs: []string{"Unarchived client: ClientByID"}, + expectError: false, + }, + { + name: "unarchive project by name", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ArchivedProject", + ClientID: client.ID, + }) + _ = q.ArchiveProject(context.Background(), project.ID) + return client.ID, project.ID + }, + args: []string{"unarchive", "--project", "ArchivedProject"}, + expectedOutputs: []string{"Unarchived project: ArchivedProject"}, + expectError: false, + }, + { + name: "unarchive project by ID", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ProjectByID", + ClientID: client.ID, + }) + _ = q.ArchiveProject(context.Background(), project.ID) + return client.ID, project.ID + }, + args: []string{"unarchive", "--project", "1"}, + expectedOutputs: []string{"Unarchived project: ProjectByID"}, + expectError: false, + }, + { + name: "unarchive without flags returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"unarchive"}, + expectError: true, + errorContains: "either --client or --project must be specified", + }, + { + name: "unarchive with both client and project flags returns error", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "TestProject", + ClientID: client.ID, + }) + _ = q.ArchiveClient(context.Background(), client.ID) + _ = q.ArchiveProject(context.Background(), project.ID) + return client.ID, project.ID + }, + args: []string{"unarchive", "--client", "TestClient", "--project", "TestProject"}, + expectError: true, + errorContains: "cannot specify both --client and --project", + }, + { + name: "unarchive nonexistent client returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"unarchive", "--client", "NonexistentClient"}, + expectError: true, + errorContains: "failed to find client", + }, + { + name: "unarchive nonexistent project returns error", + setupData: func(q *queries.Queries) (int64, int64) { return 0, 0 }, + args: []string{"unarchive", "--project", "NonexistentProject"}, + expectError: true, + errorContains: "failed to find project", + }, + { + name: "unarchive already active client succeeds", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ActiveClient", + }) + return client.ID, 0 + }, + args: []string{"unarchive", "--client", "ActiveClient"}, + expectedOutputs: []string{"Unarchived client: ActiveClient"}, + expectError: false, + }, + { + name: "unarchive client using short flag", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ShortFlagClient", + }) + _ = q.ArchiveClient(context.Background(), client.ID) + return client.ID, 0 + }, + args: []string{"unarchive", "-c", "ShortFlagClient"}, + expectedOutputs: []string{"Unarchived client: ShortFlagClient"}, + expectError: false, + }, + { + name: "unarchive project using short flag", + setupData: func(q *queries.Queries) (int64, int64) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ShortFlagProject", + ClientID: client.ID, + }) + _ = q.ArchiveProject(context.Background(), project.ID) + return client.ID, project.ID + }, + args: []string{"unarchive", "-p", "ShortFlagProject"}, + expectedOutputs: []string{"Unarchived project: ShortFlagProject"}, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q, cleanup := setupTestDB(t) + defer cleanup() + + tt.setupData(q) + + output, err := executeCommandWithDB(t, q, tt.args...) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error but got none") + } else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error()) + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + found := false + for _, expectedOutput := range tt.expectedOutputs { + if strings.Contains(output, expectedOutput) { + found = true + break + } + } + + if !found { + t.Errorf("Expected output to contain one of %v, got %q", tt.expectedOutputs, output) + } + }) + } +} + +func TestArchiveStateVerification(t *testing.T) { + tests := []struct { + name string + setupData func(*queries.Queries) (entityID int64, isClient bool) + archiveAction func(*queries.Queries, int64) + verifyFunc func(*testing.T, *queries.Queries, int64, bool) + }{ + { + name: "client is archived after archive command", + setupData: func(q *queries.Queries) (int64, bool) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + return client.ID, true + }, + archiveAction: func(q *queries.Queries, id int64) { + _ = q.ArchiveClient(context.Background(), id) + }, + verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) { + clients, err := q.FindClient(context.Background(), queries.FindClientParams{ + ID: id, + }) + if err != nil { + t.Fatalf("Failed to find client: %v", err) + } + if len(clients) != 1 { + t.Fatalf("Expected 1 client, got %d", len(clients)) + } + if clients[0].Archived == 0 { + t.Errorf("Expected client to be archived") + } + }, + }, + { + name: "project is archived after archive command", + setupData: func(q *queries.Queries) (int64, bool) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "TestProject", + ClientID: client.ID, + }) + return project.ID, false + }, + archiveAction: func(q *queries.Queries, id int64) { + _ = q.ArchiveProject(context.Background(), id) + }, + verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) { + projects, err := q.FindProject(context.Background(), queries.FindProjectParams{ + ID: id, + }) + if err != nil { + t.Fatalf("Failed to find project: %v", err) + } + if len(projects) != 1 { + t.Fatalf("Expected 1 project, got %d", len(projects)) + } + if projects[0].Archived == 0 { + t.Errorf("Expected project to be archived") + } + }, + }, + { + name: "client is unarchived after unarchive command", + setupData: func(q *queries.Queries) (int64, bool) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "ArchivedClient", + }) + _ = q.ArchiveClient(context.Background(), client.ID) + return client.ID, true + }, + archiveAction: func(q *queries.Queries, id int64) { + _ = q.UnarchiveClient(context.Background(), id) + }, + verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) { + clients, err := q.FindClient(context.Background(), queries.FindClientParams{ + ID: id, + }) + if err != nil { + t.Fatalf("Failed to find client: %v", err) + } + if len(clients) != 1 { + t.Fatalf("Expected 1 client, got %d", len(clients)) + } + if clients[0].Archived != 0 { + t.Errorf("Expected client to not be archived") + } + }, + }, + { + name: "project is unarchived after unarchive command", + setupData: func(q *queries.Queries) (int64, bool) { + client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "TestClient", + }) + project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{ + Name: "ArchivedProject", + ClientID: client.ID, + }) + _ = q.ArchiveProject(context.Background(), project.ID) + return project.ID, false + }, + archiveAction: func(q *queries.Queries, id int64) { + _ = q.UnarchiveProject(context.Background(), id) + }, + verifyFunc: func(t *testing.T, q *queries.Queries, id int64, isClient bool) { + projects, err := q.FindProject(context.Background(), queries.FindProjectParams{ + ID: id, + }) + if err != nil { + t.Fatalf("Failed to find project: %v", err) + } + if len(projects) != 1 { + t.Fatalf("Expected 1 project, got %d", len(projects)) + } + if projects[0].Archived != 0 { + t.Errorf("Expected project to not be archived") + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q, cleanup := setupTestDB(t) + defer cleanup() + + entityID, isClient := tt.setupData(q) + tt.archiveAction(q, entityID) + tt.verifyFunc(t, q, entityID, isClient) + }) + } +} |