Add 56 tests covering the core backend packages: Unit tests (no database required): - internal/partnum: 7 tests for part number generation logic (sequence, format templates, enum validation, constants) - internal/schema: 8 tests for YAML schema loading, property merging, validation, and default application Integration tests (require TEST_DATABASE_URL): - internal/db/items: 10 tests for item CRUD, archive/unarchive, revisions, and thumbnail operations - internal/db/relationships: 10 tests for BOM CRUD, cycle detection, self-reference blocking, where-used, expanded/flat BOM - internal/db/projects: 5 tests for project CRUD and item association - internal/api/bom_handlers: 6 HTTP handler tests for BOM endpoints including flat BOM, cost calculation, add/delete entries - internal/api/items: 5 HTTP handler tests for item CRUD endpoints Infrastructure: - internal/testutil: shared helpers for test DB pool setup, migration runner, and table truncation - internal/db/helpers_test.go: DB wrapper for integration tests - internal/db/db.go: add NewFromPool constructor - Makefile: add test-integration target with default DSN Integration tests skip gracefully when TEST_DATABASE_URL is unset. Dev-mode auth (nil authConfig) used for API handler tests. Fixes: fmt.Errorf Go vet warning in partnum/generator.go Closes #2
120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
func TestProjectCreate(t *testing.T) {
|
|
database := mustConnectTestDB(t)
|
|
repo := NewProjectRepository(database)
|
|
ctx := context.Background()
|
|
|
|
p := &Project{Code: "TPRJ", Name: "Test Project"}
|
|
if err := repo.Create(ctx, p); err != nil {
|
|
t.Fatalf("Create: %v", err)
|
|
}
|
|
if p.ID == "" {
|
|
t.Error("expected project ID to be set")
|
|
}
|
|
}
|
|
|
|
func TestProjectGet(t *testing.T) {
|
|
database := mustConnectTestDB(t)
|
|
repo := NewProjectRepository(database)
|
|
ctx := context.Background()
|
|
|
|
repo.Create(ctx, &Project{Code: "GPRJ", Name: "Get Project"})
|
|
|
|
got, err := repo.GetByCode(ctx, "GPRJ")
|
|
if err != nil {
|
|
t.Fatalf("GetByCode: %v", err)
|
|
}
|
|
if got == nil {
|
|
t.Fatal("expected project, got nil")
|
|
}
|
|
if got.Name != "Get Project" {
|
|
t.Errorf("name: got %q, want %q", got.Name, "Get Project")
|
|
}
|
|
|
|
// Missing should return nil
|
|
missing, err := repo.GetByCode(ctx, "NOPE")
|
|
if err != nil {
|
|
t.Fatalf("GetByCode (missing): %v", err)
|
|
}
|
|
if missing != nil {
|
|
t.Error("expected nil for missing project")
|
|
}
|
|
}
|
|
|
|
func TestProjectList(t *testing.T) {
|
|
database := mustConnectTestDB(t)
|
|
repo := NewProjectRepository(database)
|
|
ctx := context.Background()
|
|
|
|
repo.Create(ctx, &Project{Code: "AA", Name: "Alpha"})
|
|
repo.Create(ctx, &Project{Code: "BB", Name: "Beta"})
|
|
|
|
projects, err := repo.List(ctx)
|
|
if err != nil {
|
|
t.Fatalf("List: %v", err)
|
|
}
|
|
if len(projects) != 2 {
|
|
t.Errorf("expected 2 projects, got %d", len(projects))
|
|
}
|
|
}
|
|
|
|
func TestProjectDelete(t *testing.T) {
|
|
database := mustConnectTestDB(t)
|
|
repo := NewProjectRepository(database)
|
|
ctx := context.Background()
|
|
|
|
repo.Create(ctx, &Project{Code: "DEL", Name: "Deletable"})
|
|
|
|
if err := repo.Delete(ctx, "DEL"); err != nil {
|
|
t.Fatalf("Delete: %v", err)
|
|
}
|
|
|
|
got, _ := repo.GetByCode(ctx, "DEL")
|
|
if got != nil {
|
|
t.Error("deleted project should not be found")
|
|
}
|
|
}
|
|
|
|
func TestProjectItemAssociation(t *testing.T) {
|
|
database := mustConnectTestDB(t)
|
|
projRepo := NewProjectRepository(database)
|
|
itemRepo := NewItemRepository(database)
|
|
ctx := context.Background()
|
|
|
|
proj := &Project{Code: "ASSC", Name: "Assoc Project"}
|
|
projRepo.Create(ctx, proj)
|
|
|
|
item := &Item{PartNumber: "ASSC-001", ItemType: "part", Description: "associated item"}
|
|
itemRepo.Create(ctx, item, nil)
|
|
|
|
// Add item to project
|
|
if err := projRepo.AddItemToProject(ctx, item.ID, proj.ID); err != nil {
|
|
t.Fatalf("AddItemToProject: %v", err)
|
|
}
|
|
|
|
// Get items for project (takes project UUID)
|
|
items, err := projRepo.GetItemsForProject(ctx, proj.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetItemsForProject: %v", err)
|
|
}
|
|
if len(items) != 1 {
|
|
t.Errorf("expected 1 item in project, got %d", len(items))
|
|
}
|
|
|
|
// Remove item from project
|
|
if err := projRepo.RemoveItemFromProject(ctx, item.ID, proj.ID); err != nil {
|
|
t.Fatalf("RemoveItemFromProject: %v", err)
|
|
}
|
|
|
|
items, _ = projRepo.GetItemsForProject(ctx, proj.ID)
|
|
if len(items) != 0 {
|
|
t.Errorf("expected 0 items after removal, got %d", len(items))
|
|
}
|
|
}
|