summaryrefslogtreecommitdiff
path: root/internal/actions/archive_test.go
diff options
context:
space:
mode:
authorT <t@tjp.lol>2025-09-29 15:04:44 -0600
committerT <t@tjp.lol>2025-09-30 11:40:45 -0600
commit7ba68d333bc20b5795ccfd3870546a05eee60470 (patch)
tree12dc4b017803b7d01844fd42b9e3be281cbbd986 /internal/actions/archive_test.go
parentbce8dbb58165e443902d9dae3909225ef42630c4 (diff)
Support for archiving clients and projects.HEADmain
Diffstat (limited to 'internal/actions/archive_test.go')
-rw-r--r--internal/actions/archive_test.go618
1 files changed, 618 insertions, 0 deletions
diff --git a/internal/actions/archive_test.go b/internal/actions/archive_test.go
new file mode 100644
index 0000000..d205703
--- /dev/null
+++ b/internal/actions/archive_test.go
@@ -0,0 +1,618 @@
+package actions
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "testing"
+
+ "git.tjp.lol/punchcard/internal/database"
+ "git.tjp.lol/punchcard/internal/queries"
+)
+
+func setupTestDB(t *testing.T) (*queries.Queries, func()) {
+ db, err := sql.Open("sqlite", ":memory:")
+ if err != nil {
+ t.Fatalf("Failed to open in-memory sqlite db: %v", err)
+ }
+ if err := database.InitializeDB(db); err != nil {
+ t.Fatalf("Failed to initialize in-memory sqlite db: %v", err)
+ }
+ q := queries.New(db)
+
+ cleanup := func() {
+ if err := q.DBTX().(*sql.DB).Close(); err != nil {
+ t.Logf("error closing database: %v", err)
+ }
+ }
+
+ return q, cleanup
+}
+
+func TestArchiveClient(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) int64
+ expectError bool
+ }{
+ {
+ name: "archive existing client",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ return client.ID
+ },
+ expectError: false,
+ },
+ {
+ name: "archive already archived client",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "AlreadyArchived",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID
+ },
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ clientID := tt.setupData(q)
+
+ err := a.ArchiveClient(context.Background(), clientID)
+
+ if tt.expectError && err == nil {
+ t.Errorf("Expected error but got none")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if !tt.expectError {
+ client, err := a.FindClient(context.Background(), fmt.Sprintf("%d", clientID))
+ if err != nil {
+ t.Fatalf("Failed to find client: %v", err)
+ }
+ if client.Archived == 0 {
+ t.Errorf("Expected client to be archived")
+ }
+ }
+ })
+ }
+}
+
+func TestUnarchiveClient(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) int64
+ expectError bool
+ }{
+ {
+ name: "unarchive archived client",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.ID
+ },
+ expectError: false,
+ },
+ {
+ name: "unarchive already active client",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ActiveClient",
+ })
+ return client.ID
+ },
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ clientID := tt.setupData(q)
+
+ err := a.UnarchiveClient(context.Background(), clientID)
+
+ if tt.expectError && err == nil {
+ t.Errorf("Expected error but got none")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if !tt.expectError {
+ client, err := a.FindClient(context.Background(), fmt.Sprintf("%d", clientID))
+ if err != nil {
+ t.Fatalf("Failed to find client: %v", err)
+ }
+ if client.Archived != 0 {
+ t.Errorf("Expected client to not be archived")
+ }
+ }
+ })
+ }
+}
+
+func TestArchiveProject(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) int64
+ expectError bool
+ }{
+ {
+ name: "archive existing project",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "TestProject",
+ ClientID: client.ID,
+ })
+ return project.ID
+ },
+ expectError: false,
+ },
+ {
+ name: "archive already archived project",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "AlreadyArchived",
+ ClientID: client.ID,
+ })
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ return project.ID
+ },
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ projectID := tt.setupData(q)
+
+ err := a.ArchiveProject(context.Background(), projectID)
+
+ if tt.expectError && err == nil {
+ t.Errorf("Expected error but got none")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if !tt.expectError {
+ project, err := a.FindProject(context.Background(), fmt.Sprintf("%d", projectID))
+ if err != nil {
+ t.Fatalf("Failed to find project: %v", err)
+ }
+ if project.Archived == 0 {
+ t.Errorf("Expected project to be archived")
+ }
+ }
+ })
+ }
+}
+
+func TestUnarchiveProject(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) int64
+ expectError bool
+ }{
+ {
+ name: "unarchive archived project",
+ setupData: func(q *queries.Queries) 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 project.ID
+ },
+ expectError: false,
+ },
+ {
+ name: "unarchive already active project",
+ setupData: func(q *queries.Queries) int64 {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ActiveProject",
+ ClientID: client.ID,
+ })
+ return project.ID
+ },
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ projectID := tt.setupData(q)
+
+ err := a.UnarchiveProject(context.Background(), projectID)
+
+ if tt.expectError && err == nil {
+ t.Errorf("Expected error but got none")
+ }
+ if !tt.expectError && err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if !tt.expectError {
+ project, err := a.FindProject(context.Background(), fmt.Sprintf("%d", projectID))
+ if err != nil {
+ t.Fatalf("Failed to find project: %v", err)
+ }
+ if project.Archived != 0 {
+ t.Errorf("Expected project to not be archived")
+ }
+ }
+ })
+ }
+}
+
+func TestPunchInWithArchivedClient(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) (clientName string)
+ autoUnarchive bool
+ expectError bool
+ errorType error
+ }{
+ {
+ name: "punch in on archived client without auto-unarchive returns error",
+ setupData: func(q *queries.Queries) string {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.Name
+ },
+ autoUnarchive: false,
+ expectError: true,
+ errorType: ErrArchivedClient,
+ },
+ {
+ name: "punch in on archived client with auto-unarchive succeeds",
+ setupData: func(q *queries.Queries) string {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ return client.Name
+ },
+ autoUnarchive: true,
+ expectError: false,
+ },
+ {
+ name: "punch in on active client succeeds",
+ setupData: func(q *queries.Queries) string {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ActiveClient",
+ })
+ return client.Name
+ },
+ autoUnarchive: false,
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ clientName := tt.setupData(q)
+
+ session, err := a.PunchIn(context.Background(), clientName, "", "", nil, tt.autoUnarchive)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorType != nil && err != tt.errorType {
+ t.Errorf("Expected error %v, got %v", tt.errorType, err)
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if session == nil {
+ t.Fatalf("Expected session but got nil")
+ }
+
+ if tt.autoUnarchive {
+ client, err := a.FindClient(context.Background(), clientName)
+ if err != nil {
+ t.Fatalf("Failed to find client: %v", err)
+ }
+ if client.Archived != 0 {
+ t.Errorf("Expected client to be unarchived after auto-unarchive")
+ }
+ }
+ })
+ }
+}
+
+func TestPunchInWithArchivedProject(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries) (projectName string)
+ autoUnarchive bool
+ expectError bool
+ errorType error
+ }{
+ {
+ name: "punch in on archived project without auto-unarchive returns error",
+ setupData: func(q *queries.Queries) string {
+ 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.Name
+ },
+ autoUnarchive: false,
+ expectError: true,
+ errorType: ErrArchivedProject,
+ },
+ {
+ name: "punch in on archived project with auto-unarchive succeeds",
+ setupData: func(q *queries.Queries) string {
+ 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.Name
+ },
+ autoUnarchive: true,
+ expectError: false,
+ },
+ {
+ name: "punch in on active project succeeds",
+ setupData: func(q *queries.Queries) string {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ActiveProject",
+ ClientID: client.ID,
+ })
+ return project.Name
+ },
+ autoUnarchive: false,
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ projectName := tt.setupData(q)
+
+ session, err := a.PunchIn(context.Background(), "", projectName, "", nil, tt.autoUnarchive)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorType != nil && err != tt.errorType {
+ t.Errorf("Expected error %v, got %v", tt.errorType, err)
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if session == nil {
+ t.Fatalf("Expected session but got nil")
+ }
+
+ if tt.autoUnarchive {
+ project, err := a.FindProject(context.Background(), projectName)
+ if err != nil {
+ t.Fatalf("Failed to find project: %v", err)
+ }
+ if project.Archived != 0 {
+ t.Errorf("Expected project to be unarchived after auto-unarchive")
+ }
+ }
+ })
+ }
+}
+
+func TestPunchInMostRecentWithArchivedClient(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries)
+ autoUnarchive bool
+ expectError bool
+ errorType error
+ }{
+ {
+ name: "punch in most recent with archived client without auto-unarchive returns error",
+ setupData: func(q *queries.Queries) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _, _ = q.CreateTimeEntry(context.Background(), queries.CreateTimeEntryParams{
+ ClientID: client.ID,
+ })
+ _, _ = q.StopTimeEntry(context.Background())
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ },
+ autoUnarchive: false,
+ expectError: true,
+ errorType: ErrArchivedClient,
+ },
+ {
+ name: "punch in most recent with archived client with auto-unarchive succeeds",
+ setupData: func(q *queries.Queries) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "ArchivedClient",
+ })
+ _, _ = q.CreateTimeEntry(context.Background(), queries.CreateTimeEntryParams{
+ ClientID: client.ID,
+ })
+ _, _ = q.StopTimeEntry(context.Background())
+ _ = q.ArchiveClient(context.Background(), client.ID)
+ },
+ autoUnarchive: true,
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ tt.setupData(q)
+
+ session, err := a.PunchInMostRecent(context.Background(), "", nil, tt.autoUnarchive)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorType != nil && err != tt.errorType {
+ t.Errorf("Expected error %v, got %v", tt.errorType, err)
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if session == nil {
+ t.Fatalf("Expected session but got nil")
+ }
+ })
+ }
+}
+
+func TestPunchInMostRecentWithArchivedProject(t *testing.T) {
+ tests := []struct {
+ name string
+ setupData func(*queries.Queries)
+ autoUnarchive bool
+ expectError bool
+ errorType error
+ }{
+ {
+ name: "punch in most recent with archived project without auto-unarchive returns error",
+ setupData: func(q *queries.Queries) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ArchivedProject",
+ ClientID: client.ID,
+ })
+ _, _ = q.CreateTimeEntry(context.Background(), queries.CreateTimeEntryParams{
+ ClientID: client.ID,
+ ProjectID: sql.NullInt64{Int64: project.ID, Valid: true},
+ })
+ _, _ = q.StopTimeEntry(context.Background())
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ },
+ autoUnarchive: false,
+ expectError: true,
+ errorType: ErrArchivedProject,
+ },
+ {
+ name: "punch in most recent with archived project with auto-unarchive succeeds",
+ setupData: func(q *queries.Queries) {
+ client, _ := q.CreateClient(context.Background(), queries.CreateClientParams{
+ Name: "TestClient",
+ })
+ project, _ := q.CreateProject(context.Background(), queries.CreateProjectParams{
+ Name: "ArchivedProject",
+ ClientID: client.ID,
+ })
+ _, _ = q.CreateTimeEntry(context.Background(), queries.CreateTimeEntryParams{
+ ClientID: client.ID,
+ ProjectID: sql.NullInt64{Int64: project.ID, Valid: true},
+ })
+ _, _ = q.StopTimeEntry(context.Background())
+ _ = q.ArchiveProject(context.Background(), project.ID)
+ },
+ autoUnarchive: true,
+ expectError: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ q, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ a := New(q)
+ tt.setupData(q)
+
+ session, err := a.PunchInMostRecent(context.Background(), "", nil, tt.autoUnarchive)
+
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Expected error but got none")
+ } else if tt.errorType != nil && err != tt.errorType {
+ t.Errorf("Expected error %v, got %v", tt.errorType, err)
+ }
+ return
+ }
+
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ if session == nil {
+ t.Fatalf("Expected session but got nil")
+ }
+ })
+ }
+}