package certificate import ( "os" "path/filepath" "testing" "git.secnex.io/secnex/certman/models" "github.com/google/uuid" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func setupTestDatabase(t *testing.T) (*gorm.DB, func()) { // Create temporary database file dbPath := filepath.Join(t.TempDir(), "test_certman.db") db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) if err != nil { t.Fatalf("Failed to connect to database: %v", err) } // Auto-migrate models if err := db.AutoMigrate( &models.Organization{}, &models.CertificateAuthority{}, ); err != nil { t.Fatalf("Failed to migrate database: %v", err) } cleanup := func() { os.Remove(dbPath) } return db, cleanup } func setupTestService(t *testing.T) (*CertificateAuthorityService, *gorm.DB, func()) { db, dbCleanup := setupTestDatabase(t) certDir := filepath.Join(t.TempDir(), "certs") privateDir := filepath.Join(t.TempDir(), "private") service := NewCertificateAuthorityService(db, certDir, privateDir) cleanup := func() { dbCleanup() os.RemoveAll(certDir) os.RemoveAll(privateDir) } return service, db, cleanup } func createTestOrganization(t *testing.T, db *gorm.DB) *models.Organization { org := &models.Organization{ Name: "Test Organization", Description: "Test Organization for CA tests", Status: models.OrganizationStatusActive, } if err := db.Create(org).Error; err != nil { t.Fatalf("Failed to create test organization: %v", err) } return org } func TestCreateRootCA(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } ca, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } if ca == nil { t.Fatal("Created CA should not be nil") } if !ca.Root { t.Error("Created CA should be marked as root") } if ca.ParentID != nil { t.Error("Root CA should not have a parent ID") } if ca.Name != req.Name { t.Errorf("Expected name %s, got %s", req.Name, ca.Name) } if ca.AttributeCommonName != req.CommonName { t.Errorf("Expected common name %s, got %s", req.CommonName, ca.AttributeCommonName) } if ca.FileID == "" { t.Error("Certificate file ID should not be empty") } if ca.PrivateKeyID == "" { t.Error("Private key file ID should not be empty") } } func TestCreateIntermediateCA(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // First create a root CA rootReq := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } rootCA, err := service.CreateRootCA(rootReq) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Now create an intermediate CA req := &CreateIntermediateCARequest{ Name: "Test Intermediate CA", Description: "Test Intermediate CA for testing", CommonName: "Test Intermediate CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "intermediate@example.com", OrganizationID: org.ID, ParentCAID: rootCA.ID, } ca, err := service.CreateIntermediateCA(req) if err != nil { t.Fatalf("Failed to create intermediate CA: %v", err) } if ca == nil { t.Fatal("Created CA should not be nil") } if ca.Root { t.Error("Created CA should not be marked as root") } if ca.ParentID == nil { t.Error("Intermediate CA should have a parent ID") } if ca.ParentID.String() != rootCA.ID.String() { t.Errorf("Expected parent ID %s, got %s", rootCA.ID.String(), ca.ParentID.String()) } if ca.Name != req.Name { t.Errorf("Expected name %s, got %s", req.Name, ca.Name) } if ca.AttributeCommonName != req.CommonName { t.Errorf("Expected common name %s, got %s", req.CommonName, ca.AttributeCommonName) } if ca.FileID == "" { t.Error("Certificate file ID should not be empty") } if ca.PrivateKeyID == "" { t.Error("Private key file ID should not be empty") } } func TestGetCA(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } createdCA, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Retrieve the CA retrievedCA, err := service.GetCA(createdCA.ID.String()) if err != nil { t.Fatalf("Failed to get CA: %v", err) } if retrievedCA.ID != createdCA.ID { t.Errorf("Expected ID %s, got %s", createdCA.ID.String(), retrievedCA.ID.String()) } if retrievedCA.Name != createdCA.Name { t.Errorf("Expected name %s, got %s", createdCA.Name, retrievedCA.Name) } } func TestGetAllCAs(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create multiple CAs rootReq := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } rootCA, err := service.CreateRootCA(rootReq) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } intermediateReq := &CreateIntermediateCARequest{ Name: "Test Intermediate CA", Description: "Test Intermediate CA for testing", CommonName: "Test Intermediate CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "intermediate@example.com", OrganizationID: org.ID, ParentCAID: rootCA.ID, } _, err = service.CreateIntermediateCA(intermediateReq) if err != nil { t.Fatalf("Failed to create intermediate CA: %v", err) } // Get all CAs allCAs, err := service.GetAllCAs() if err != nil { t.Fatalf("Failed to get all CAs: %v", err) } if len(allCAs) != 2 { t.Errorf("Expected 2 CAs, got %d", len(allCAs)) } // Get root CAs rootCAs, err := service.GetRootCAs() if err != nil { t.Fatalf("Failed to get root CAs: %v", err) } if len(rootCAs) != 1 { t.Errorf("Expected 1 root CA, got %d", len(rootCAs)) } // Get intermediate CAs intermediateCAs, err := service.GetIntermediateCAs() if err != nil { t.Fatalf("Failed to get intermediate CAs: %v", err) } if len(intermediateCAs) != 1 { t.Errorf("Expected 1 intermediate CA, got %d", len(intermediateCAs)) } } func TestGetCACertificate(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } ca, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Get CA certificate cert, err := service.GetCACertificate(ca.ID.String()) if err != nil { t.Fatalf("Failed to get CA certificate: %v", err) } if cert == nil { t.Fatal("Certificate should not be nil") } if cert.Subject.CommonName != req.CommonName { t.Errorf("Expected common name %s, got %s", req.CommonName, cert.Subject.CommonName) } if !cert.IsCA { t.Error("Certificate should be marked as CA") } } func TestGetCAPrivateKey(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } ca, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Get CA private key privateKey, err := service.GetCAPrivateKey(ca.ID.String()) if err != nil { t.Fatalf("Failed to get CA private key: %v", err) } if privateKey == nil { t.Fatal("Private key should not be nil") } } func TestValidateCAChain(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA rootReq := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } rootCA, err := service.CreateRootCA(rootReq) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Validate root CA chain if err := service.ValidateCAChain(rootCA.ID.String()); err != nil { t.Fatalf("Failed to validate root CA chain: %v", err) } // Create an intermediate CA intermediateReq := &CreateIntermediateCARequest{ Name: "Test Intermediate CA", Description: "Test Intermediate CA for testing", CommonName: "Test Intermediate CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "intermediate@example.com", OrganizationID: org.ID, ParentCAID: rootCA.ID, } intermediateCA, err := service.CreateIntermediateCA(intermediateReq) if err != nil { t.Fatalf("Failed to create intermediate CA: %v", err) } // Validate intermediate CA chain if err := service.ValidateCAChain(intermediateCA.ID.String()); err != nil { t.Fatalf("Failed to validate intermediate CA chain: %v", err) } } func TestRevokeCA(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } ca, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Revoke CA reason := "Test revocation" if err := service.RevokeCA(ca.ID.String(), reason); err != nil { t.Fatalf("Failed to revoke CA: %v", err) } // Verify CA was updated updatedCA, err := service.GetCA(ca.ID.String()) if err != nil { t.Fatalf("Failed to get updated CA: %v", err) } if updatedCA.Description == ca.Description { t.Error("CA description should have been updated with revocation info") } } func TestDeleteCA(t *testing.T) { service, db, cleanup := setupTestService(t) defer cleanup() org := createTestOrganization(t, db) // Create a root CA req := &CreateRootCARequest{ Name: "Test Root CA", Description: "Test Root CA for testing", CommonName: "Test Root CA", Organization: "Test Organization", OrganizationalUnit: "IT Department", Country: "DE", State: "Bavaria", Locality: "Munich", Street: "Test Street 1", Address: "Test Street 1, 80331 Munich", PostalCode: "80331", Email: "test@example.com", OrganizationID: org.ID, } ca, err := service.CreateRootCA(req) if err != nil { t.Fatalf("Failed to create root CA: %v", err) } // Delete CA if err := service.DeleteCA(ca.ID.String()); err != nil { t.Fatalf("Failed to delete CA: %v", err) } // Verify CA was deleted _, err = service.GetCA(ca.ID.String()) if err == nil { t.Error("CA should have been deleted") } } func TestValidateRootCARequest(t *testing.T) { service, _, cleanup := setupTestService(t) defer cleanup() tests := []struct { name string req *CreateRootCARequest wantErr bool }{ { name: "valid request", req: &CreateRootCARequest{ Name: "Test CA", CommonName: "Test CA", Organization: "Test Org", Country: "DE", OrganizationID: uuid.New(), }, wantErr: false, }, { name: "missing name", req: &CreateRootCARequest{ CommonName: "Test CA", Organization: "Test Org", Country: "DE", OrganizationID: uuid.New(), }, wantErr: true, }, { name: "missing common name", req: &CreateRootCARequest{ Name: "Test CA", Organization: "Test Org", Country: "DE", OrganizationID: uuid.New(), }, wantErr: true, }, { name: "missing organization", req: &CreateRootCARequest{ Name: "Test CA", CommonName: "Test CA", Country: "DE", OrganizationID: uuid.New(), }, wantErr: true, }, { name: "missing country", req: &CreateRootCARequest{ Name: "Test CA", CommonName: "Test CA", Organization: "Test Org", OrganizationID: uuid.New(), }, wantErr: true, }, { name: "missing organization ID", req: &CreateRootCARequest{ Name: "Test CA", CommonName: "Test CA", Organization: "Test Org", Country: "DE", }, wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := service.validateRootCARequest(test.req) if (err != nil) != test.wantErr { t.Errorf("validateRootCARequest() error = %v, wantErr %v", err, test.wantErr) } }) } } func TestValidateIntermediateCARequest(t *testing.T) { service, _, cleanup := setupTestService(t) defer cleanup() tests := []struct { name string req *CreateIntermediateCARequest wantErr bool }{ { name: "valid request", req: &CreateIntermediateCARequest{ Name: "Test CA", CommonName: "Test CA", Organization: "Test Org", Country: "DE", OrganizationID: uuid.New(), ParentCAID: uuid.New(), }, wantErr: false, }, { name: "missing parent CA ID", req: &CreateIntermediateCARequest{ Name: "Test CA", CommonName: "Test CA", Organization: "Test Org", Country: "DE", OrganizationID: uuid.New(), }, wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { err := service.validateIntermediateCARequest(test.req) if (err != nil) != test.wantErr { t.Errorf("validateIntermediateCARequest() error = %v, wantErr %v", err, test.wantErr) } }) } }