Files
schemas/domain/commands_test.go
T
argoyle 862060875b test: add validation and event tests for organization and API key
Adds unit tests for the `AddOrganization` and `AddAPIKey` commands. 
These tests validate various scenarios, including success cases, 
handling of already existing organizations or keys, and ensuring 
required fields are checked. The
changes enhance test coverage and ensure robustness of the command 
logic.
2025-11-21 12:49:23 +01:00

467 lines
12 KiB
Go

package domain
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
"gitlab.com/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)
}