Files
silo/internal/db/projects_test.go
Forbes d08b178466 test: add comprehensive test suite for backend
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
2026-02-07 01:57:10 -06:00

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))
}
}