Files
schemas/domain/commands_test.go
T
argoyle bba1e8eba7
schemas / check-release (pull_request) Failing after 1m31s
schemas / vulnerabilities (pull_request) Successful in 2m19s
schemas / check (pull_request) Successful in 4m28s
schemas / build (pull_request) Has been skipped
schemas / deploy-prod (pull_request) Has been skipped
pre-commit / pre-commit (pull_request) Successful in 6m3s
feat: migrate from GitLab CI to Gitea Actions
- Update git remote to git.unbound.se
- Add Gitea workflows: ci.yaml, pre-commit.yaml, release.yaml, goreleaser.yaml
- Delete .gitlab-ci.yml
- Update Go module path to gitea.unbound.se/unboundsoftware/schemas
- Update all imports to new module path
- Update Docker registry to oci.unbound.se
- Update .goreleaser.yml for Gitea releases with internal cluster URL
- Remove GitLab CI linter from pre-commit config
- Use shared release workflow with tag_only for versioning

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 22:17:02 +01:00

578 lines
15 KiB
Go

package domain
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitea.unbound.se/unboundsoftware/schemas/hash"
)
// AddOrganization tests
func TestAddOrganization_Validate_Success(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
org := &Organization{} // New organization with no identity
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestAddOrganization_Validate_AlreadyExists(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("existing-org-id"),
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "already exists")
}
func TestAddOrganization_Validate_EmptyName(t *testing.T) {
cmd := AddOrganization{
Name: "",
Initiator: "user@example.com",
}
org := &Organization{}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "name is required")
}
func TestAddOrganization_Event(t *testing.T) {
cmd := AddOrganization{
Name: "Test Org",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
orgEvent, ok := event.(*OrganizationAdded)
require.True(t, ok)
assert.Equal(t, "Test Org", orgEvent.Name)
assert.Equal(t, "user@example.com", orgEvent.Initiator)
}
// AddAPIKey tests
func TestAddAPIKey_Event(t *testing.T) {
type fields struct {
Name string
Key string
Refs []string
Read bool
Publish bool
Initiator string
}
type args struct {
in0 context.Context
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "event",
fields: fields{
Name: "test",
Key: "us_ak_1234567890123456",
Refs: []string{"Example@dev"},
Read: true,
Publish: true,
Initiator: "jim@example.org",
},
args: args{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := AddAPIKey{
Name: tt.fields.Name,
Key: tt.fields.Key,
Refs: tt.fields.Refs,
Read: tt.fields.Read,
Publish: tt.fields.Publish,
Initiator: tt.fields.Initiator,
}
event := a.Event(tt.args.in0)
require.NotNil(t, event)
// Cast to APIKeyAdded to verify fields
apiKeyEvent, ok := event.(*APIKeyAdded)
require.True(t, ok, "Event should be *APIKeyAdded")
// Verify non-key fields match exactly
assert.Equal(t, tt.fields.Name, apiKeyEvent.Name)
assert.Equal(t, tt.fields.Refs, apiKeyEvent.Refs)
assert.Equal(t, tt.fields.Read, apiKeyEvent.Read)
assert.Equal(t, tt.fields.Publish, apiKeyEvent.Publish)
assert.Equal(t, tt.fields.Initiator, apiKeyEvent.Initiator)
// Verify the key is hashed correctly (bcrypt format)
assert.True(t, strings.HasPrefix(apiKeyEvent.Key, "$2"), "Key should be bcrypt hashed")
assert.NotEqual(t, tt.fields.Key, apiKeyEvent.Key, "Key should be hashed, not plaintext")
// Verify the hash matches the original key
assert.True(t, hash.CompareAPIKey(apiKeyEvent.Key, tt.fields.Key), "Hashed key should match original")
})
}
}
func TestAddAPIKey_Validate_Success(t *testing.T) {
cmd := AddAPIKey{
Name: "production-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{},
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestAddAPIKey_Validate_OrganizationNotExists(t *testing.T) {
cmd := AddAPIKey{
Name: "production-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestAddAPIKey_Validate_DuplicateKeyName(t *testing.T) {
cmd := AddAPIKey{
Name: "existing-key",
Key: "us_ak_1234567890123456",
Refs: []string{"main"},
Read: true,
Publish: false,
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "existing-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "already exist")
assert.Contains(t, err.Error(), "existing-key")
}
// UpdateSubGraph tests
func TestUpdateSubGraph_Validate_Success(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
assert.NoError(t, err)
}
func TestUpdateSubGraph_Validate_MissingRef(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "ref is missing")
}
func TestUpdateSubGraph_Validate_RefWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: " ",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "ref is missing")
}
func TestUpdateSubGraph_Validate_MissingService(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "service is missing")
}
func TestUpdateSubGraph_Validate_ServiceWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: " ",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "service is missing")
}
func TestUpdateSubGraph_Validate_MissingSDL(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "SDL is missing")
}
func TestUpdateSubGraph_Validate_SDLWhitespaceOnly(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: " \n\t ",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "SDL is missing")
}
func TestUpdateSubGraph_Validate_MissingURL_NoExistingURL(t *testing.T) {
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: nil,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil, // No existing URL
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_MissingURL_HasExistingURL(t *testing.T) {
existingURL := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: nil,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: &existingURL, // Has existing URL, so nil is OK
}
err := cmd.Validate(context.Background(), subGraph)
assert.NoError(t, err)
}
func TestUpdateSubGraph_Validate_EmptyURL_NoExistingURL(t *testing.T) {
emptyURL := ""
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &emptyURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil,
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_URLWhitespaceOnly_NoExistingURL(t *testing.T) {
whitespaceURL := " "
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &whitespaceURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
subGraph := &SubGraph{
BaseAggregate: eventsourced.BaseAggregateFromString("subgraph-123"),
Url: nil,
}
err := cmd.Validate(context.Background(), subGraph)
require.Error(t, err)
assert.Contains(t, err.Error(), "url is missing")
}
func TestUpdateSubGraph_Validate_WrongAggregateType(t *testing.T) {
url := "http://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
// Pass wrong aggregate type
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "not a SubGraph")
}
func TestUpdateSubGraph_Event(t *testing.T) {
url := "http://example.com/graphql"
wsURL := "ws://example.com/graphql"
cmd := UpdateSubGraph{
OrganizationId: "org-123",
Ref: "main",
Service: "users",
Url: &url,
WSUrl: &wsURL,
Sdl: "type Query { hello: String }",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
subGraphEvent, ok := event.(*SubGraphUpdated)
require.True(t, ok)
assert.Equal(t, "org-123", subGraphEvent.OrganizationId)
assert.Equal(t, "main", subGraphEvent.Ref)
assert.Equal(t, "users", subGraphEvent.Service)
assert.Equal(t, url, *subGraphEvent.Url)
assert.Equal(t, wsURL, *subGraphEvent.WSUrl)
assert.Equal(t, "type Query { hello: String }", subGraphEvent.Sdl)
assert.Equal(t, "user@example.com", subGraphEvent.Initiator)
}
// RemoveAPIKey tests
func TestRemoveAPIKey_Validate_Success(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "production-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestRemoveAPIKey_Validate_OrganizationNotExists(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestRemoveAPIKey_Validate_KeyNotFound(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "non-existent-key",
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
APIKeys: []APIKey{
{
Name: "production-key",
Key: "hashed-key",
},
},
}
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "not found")
assert.Contains(t, err.Error(), "non-existent-key")
}
func TestRemoveAPIKey_Event(t *testing.T) {
cmd := RemoveAPIKey{
KeyName: "production-key",
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
keyEvent, ok := event.(*APIKeyRemoved)
require.True(t, ok)
assert.Equal(t, "production-key", keyEvent.KeyName)
assert.Equal(t, "user@example.com", keyEvent.Initiator)
}
// RemoveOrganization tests
func TestRemoveOrganization_Validate_Success(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
org := &Organization{
BaseAggregate: eventsourced.BaseAggregateFromString("org-123"),
Name: "Test Org",
}
err := cmd.Validate(context.Background(), org)
assert.NoError(t, err)
}
func TestRemoveOrganization_Validate_OrganizationNotExists(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
org := &Organization{} // No identity means it doesn't exist
err := cmd.Validate(context.Background(), org)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not exist")
}
func TestRemoveOrganization_Event(t *testing.T) {
cmd := RemoveOrganization{
Initiator: "user@example.com",
}
event := cmd.Event(context.Background())
require.NotNil(t, event)
orgEvent, ok := event.(*OrganizationRemoved)
require.True(t, ok)
assert.Equal(t, "user@example.com", orgEvent.Initiator)
}