package unit import ( "testing" "time" "autostore/internal/domain/specifications" ) // Test data constants const ( TEST_STRING = "test-value" TEST_INT = 42 TEST_FLOAT = 3.14 TEST_DATE = "2023-01-15 10:30:00" TEST_DATE_2 = "2023-02-15 10:30:00" INVALID_DATE = "invalid-date" ) // TestObject is a simple struct for testing specifications type TestObject struct { name string age int price float64 status string score int role string department string active bool optionalField interface{} dateField string createdAt time.Time expirationDate time.Time } // NewTestObject creates a new TestObject with the given field values func NewTestObject(fields map[string]interface{}) *TestObject { obj := &TestObject{} if val, ok := fields["name"]; ok { obj.name = val.(string) } if val, ok := fields["age"]; ok { obj.age = val.(int) } if val, ok := fields["price"]; ok { obj.price = val.(float64) } if val, ok := fields["status"]; ok { obj.status = val.(string) } if val, ok := fields["score"]; ok { obj.score = val.(int) } if val, ok := fields["role"]; ok { obj.role = val.(string) } if val, ok := fields["department"]; ok { obj.department = val.(string) } if val, ok := fields["active"]; ok { obj.active = val.(bool) } if val, ok := fields["optionalField"]; ok { obj.optionalField = val } if val, ok := fields["dateField"]; ok { obj.dateField = val.(string) } if val, ok := fields["createdAt"]; ok { switch v := val.(type) { case time.Time: obj.createdAt = v case string: obj.createdAt, _ = time.Parse(dateFormat, v) } } if val, ok := fields["expirationDate"]; ok { switch v := val.(type) { case time.Time: obj.expirationDate = v case string: obj.expirationDate, _ = time.Parse(dateFormat, v) } } return obj } // Getter methods for TestObject func (o *TestObject) GetName() string { return o.name } func (o *TestObject) GetAge() int { return o.age } func (o *TestObject) GetPrice() float64 { return o.price } func (o *TestObject) GetStatus() string { return o.status } func (o *TestObject) GetScore() int { return o.score } func (o *TestObject) GetRole() string { return o.role } func (o *TestObject) GetDepartment() string { return o.department } func (o *TestObject) GetActive() bool { return o.active } func (o *TestObject) GetOptionalField() interface{} { return o.optionalField } func (o *TestObject) GetDateField() string { return o.dateField } func (o *TestObject) GetCreatedAt() time.Time { return o.createdAt } func (o *TestObject) GetExpirationDate() time.Time { return o.expirationDate } // EQ Operator Tests func TestWhenUsingEqWithStringThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("name", TEST_STRING)) matchingObject := NewTestObject(map[string]interface{}{"name": TEST_STRING}) nonMatchingObject := NewTestObject(map[string]interface{}{"name": "different"}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingEqWithIntegerThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("age", TEST_INT)) matchingObject := NewTestObject(map[string]interface{}{"age": TEST_INT}) nonMatchingObject := NewTestObject(map[string]interface{}{"age": 100}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingEqWithFloatThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("price", TEST_FLOAT)) matchingObject := NewTestObject(map[string]interface{}{"price": TEST_FLOAT}) nonMatchingObject := NewTestObject(map[string]interface{}{"price": 1.0}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // NEQ Operator Tests func TestWhenUsingNeqThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Neq("status", "inactive")) matchingObject := NewTestObject(map[string]interface{}{"status": "active"}) nonMatchingObject := NewTestObject(map[string]interface{}{"status": "inactive"}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // Comparison Operators Tests func TestWhenUsingGtThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Gt("score", 80)) matchingObject := NewTestObject(map[string]interface{}{"score": 90}) nonMatchingObject := NewTestObject(map[string]interface{}{"score": 70}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingGteThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Gte("score", 80)) matchingObject1 := NewTestObject(map[string]interface{}{"score": 80}) matchingObject2 := NewTestObject(map[string]interface{}{"score": 90}) nonMatchingObject := NewTestObject(map[string]interface{}{"score": 70}) // When matchResult1 := spec.IsSatisfiedBy(matchingObject1) matchResult2 := spec.IsSatisfiedBy(matchingObject2) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult1 { t.Error("Expected matching object 1 to satisfy the specification") } if !matchResult2 { t.Error("Expected matching object 2 to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingLtThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Lt("score", 80)) matchingObject := NewTestObject(map[string]interface{}{"score": 70}) nonMatchingObject := NewTestObject(map[string]interface{}{"score": 90}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingLteThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Lte("score", 80)) matchingObject1 := NewTestObject(map[string]interface{}{"score": 80}) matchingObject2 := NewTestObject(map[string]interface{}{"score": 70}) nonMatchingObject := NewTestObject(map[string]interface{}{"score": 90}) // When matchResult1 := spec.IsSatisfiedBy(matchingObject1) matchResult2 := spec.IsSatisfiedBy(matchingObject2) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult1 { t.Error("Expected matching object 1 to satisfy the specification") } if !matchResult2 { t.Error("Expected matching object 2 to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // IN Operator Tests func TestWhenUsingInThenMatchesCorrectly(t *testing.T) { // Given validValues := []interface{}{"admin", "moderator", "editor"} spec := specifications.NewSimpleSpecification[*TestObject](specifications.In("role", validValues)) matchingObject := NewTestObject(map[string]interface{}{"role": "admin"}) nonMatchingObject := NewTestObject(map[string]interface{}{"role": "user"}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingInWithEmptyArrayThenNeverMatches(t *testing.T) { // Given validValues := []interface{}{} spec := specifications.NewSimpleSpecification[*TestObject](specifications.In("role", validValues)) testObject := NewTestObject(map[string]interface{}{"role": "admin"}) // When result := spec.IsSatisfiedBy(testObject) // Then if result { t.Error("Expected object to not satisfy the specification with empty array") } } // NOT IN Operator Tests func TestWhenUsingNotInThenMatchesCorrectly(t *testing.T) { // Given invalidValues := []interface{}{"banned", "suspended"} spec := specifications.NewSimpleSpecification[*TestObject](specifications.Nin("status", invalidValues)) matchingObject := NewTestObject(map[string]interface{}{"status": "active"}) nonMatchingObject := NewTestObject(map[string]interface{}{"status": "banned"}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // DateTime Tests func TestWhenUsingDateTimeComparisonWithStringsThenMatchesCorrectly(t *testing.T) { // Given testDate2, _ := time.Parse(dateFormat, TEST_DATE_2) spec := specifications.NewSimpleSpecification[*TestObject](specifications.Lt("createdAt", testDate2)) matchingObject := NewTestObject(map[string]interface{}{"createdAt": TEST_DATE}) nonMatchingObject := NewTestObject(map[string]interface{}{"createdAt": TEST_DATE_2}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingDateTimeComparisonWithTimeObjectsThenMatchesCorrectly(t *testing.T) { // Given testDate, _ := time.Parse(dateFormat, TEST_DATE) spec := specifications.NewSimpleSpecification[*TestObject](specifications.Lte("expirationDate", testDate)) matchingDate, _ := time.Parse(dateFormat, TEST_DATE) nonMatchingDate, _ := time.Parse(dateFormat, TEST_DATE_2) matchingObject := NewTestObject(map[string]interface{}{"expirationDate": matchingDate}) nonMatchingObject := NewTestObject(map[string]interface{}{"expirationDate": nonMatchingDate}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // AND Group Tests func TestWhenUsingAndGroupThenMatchesOnlyWhenAllConditionsMet(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.And( specifications.Eq("status", "active"), specifications.Gte("score", 80), specifications.In("role", []interface{}{"admin", "moderator"}), )) matchingObject := NewTestObject(map[string]interface{}{ "status": "active", "score": 85, "role": "admin", }) nonMatchingObject1 := NewTestObject(map[string]interface{}{ "status": "inactive", "score": 85, "role": "admin", }) nonMatchingObject2 := NewTestObject(map[string]interface{}{ "status": "active", "score": 70, "role": "admin", }) nonMatchingObject3 := NewTestObject(map[string]interface{}{ "status": "active", "score": 85, "role": "user", }) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult1 := spec.IsSatisfiedBy(nonMatchingObject1) noMatchResult2 := spec.IsSatisfiedBy(nonMatchingObject2) noMatchResult3 := spec.IsSatisfiedBy(nonMatchingObject3) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult1 { t.Error("Expected non-matching object 1 to not satisfy the specification") } if noMatchResult2 { t.Error("Expected non-matching object 2 to not satisfy the specification") } if noMatchResult3 { t.Error("Expected non-matching object 3 to not satisfy the specification") } } // OR Group Tests func TestWhenUsingOrGroupThenMatchesWhenAnyConditionMet(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Or( specifications.Eq("role", "admin"), specifications.Gte("score", 90), specifications.In("department", []interface{}{"IT", "HR"}), )) matchingObject1 := NewTestObject(map[string]interface{}{ "role": "admin", "score": 70, "department": "Finance", }) matchingObject2 := NewTestObject(map[string]interface{}{ "role": "user", "score": 95, "department": "Finance", }) matchingObject3 := NewTestObject(map[string]interface{}{ "role": "user", "score": 70, "department": "IT", }) nonMatchingObject := NewTestObject(map[string]interface{}{ "role": "user", "score": 70, "department": "Finance", }) // When matchResult1 := spec.IsSatisfiedBy(matchingObject1) matchResult2 := spec.IsSatisfiedBy(matchingObject2) matchResult3 := spec.IsSatisfiedBy(matchingObject3) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult1 { t.Error("Expected matching object 1 to satisfy the specification") } if !matchResult2 { t.Error("Expected matching object 2 to satisfy the specification") } if !matchResult3 { t.Error("Expected matching object 3 to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // NOT Group Tests func TestWhenUsingNotGroupThenInvertsCondition(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Not(specifications.Eq("status", "banned"))) matchingObject := NewTestObject(map[string]interface{}{"status": "active"}) nonMatchingObject := NewTestObject(map[string]interface{}{"status": "banned"}) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } // Complex Nested Groups Tests func TestWhenUsingNestedAndOrGroupsThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.And( specifications.Eq("status", "active"), specifications.Or( specifications.Gte("score", 80), specifications.In("role", []interface{}{"admin", "moderator"}), ), )) matchingObject1 := NewTestObject(map[string]interface{}{ "status": "active", "score": 85, "role": "user", }) matchingObject2 := NewTestObject(map[string]interface{}{ "status": "active", "score": 70, "role": "admin", }) nonMatchingObject := NewTestObject(map[string]interface{}{ "status": "inactive", "score": 85, "role": "user", }) // When matchResult1 := spec.IsSatisfiedBy(matchingObject1) matchResult2 := spec.IsSatisfiedBy(matchingObject2) noMatchResult := spec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult1 { t.Error("Expected matching object 1 to satisfy the specification") } if !matchResult2 { t.Error("Expected matching object 2 to satisfy the specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the specification") } } func TestWhenUsingTripleNestedGroupsThenMatchesCorrectly(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.And( specifications.Eq("active", true), specifications.Not(specifications.Or( specifications.Eq("role", "banned"), specifications.Eq("status", "suspended"), )), )) matchingObject := NewTestObject(map[string]interface{}{ "active": true, "role": "user", "status": "active", }) nonMatchingObject1 := NewTestObject(map[string]interface{}{ "active": false, "role": "user", "status": "active", }) nonMatchingObject2 := NewTestObject(map[string]interface{}{ "active": true, "role": "banned", "status": "active", }) // When matchResult := spec.IsSatisfiedBy(matchingObject) noMatchResult1 := spec.IsSatisfiedBy(nonMatchingObject1) noMatchResult2 := spec.IsSatisfiedBy(nonMatchingObject2) // Then if !matchResult { t.Error("Expected matching object to satisfy the specification") } if noMatchResult1 { t.Error("Expected non-matching object 1 to not satisfy the specification") } if noMatchResult2 { t.Error("Expected non-matching object 2 to not satisfy the specification") } } // Edge Case Tests func TestWhenFieldDoesNotExistThenReturnsFalse(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("nonExistentField", "value")) testObject := NewTestObject(map[string]interface{}{"existingField": "value"}) // When result := spec.IsSatisfiedBy(testObject) // Then if result { t.Error("Expected object to not satisfy the specification when field doesn't exist") } } func TestWhenFieldIsNilThenReturnsFalse(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("optionalField", "value")) testObject := NewTestObject(map[string]interface{}{"optionalField": nil}) // When result := spec.IsSatisfiedBy(testObject) // Then if result { t.Error("Expected object to not satisfy the specification when field is nil") } } func TestWhenUsingInvalidDateStringThenFallsBackToRegularComparison(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("dateField", INVALID_DATE)) testObject := NewTestObject(map[string]interface{}{"dateField": INVALID_DATE}) // When result := spec.IsSatisfiedBy(testObject) // Then if !result { t.Error("Expected object to satisfy the specification with invalid date string") } } // Spec Helper Method Tests func TestWhenUsingSpecHelpersThenCreatesCorrectSpecification(t *testing.T) { // Given eqSpec := specifications.Eq("field", "value") neqSpec := specifications.Neq("field", "value") gtSpec := specifications.Gt("field", 10) gteSpec := specifications.Gte("field", 10) ltSpec := specifications.Lt("field", 10) lteSpec := specifications.Lte("field", 10) inSpec := specifications.In("field", []interface{}{"a", "b"}) ninSpec := specifications.Nin("field", []interface{}{"a", "b"}) // When & Then if eqSpec.Condition == nil || eqSpec.Condition.Operator != specifications.OP_EQ || eqSpec.Condition.Value != "value" { t.Error("EQ spec not created correctly") } if neqSpec.Condition == nil || neqSpec.Condition.Operator != specifications.OP_NEQ || neqSpec.Condition.Value != "value" { t.Error("NEQ spec not created correctly") } if gtSpec.Condition == nil || gtSpec.Condition.Operator != specifications.OP_GT || gtSpec.Condition.Value != 10 { t.Error("GT spec not created correctly") } if gteSpec.Condition == nil || gteSpec.Condition.Operator != specifications.OP_GTE || gteSpec.Condition.Value != 10 { t.Error("GTE spec not created correctly") } if ltSpec.Condition == nil || ltSpec.Condition.Operator != specifications.OP_LT || ltSpec.Condition.Value != 10 { t.Error("LT spec not created correctly") } if lteSpec.Condition == nil || lteSpec.Condition.Operator != specifications.OP_LTE || lteSpec.Condition.Value != 10 { t.Error("LTE spec not created correctly") } if inSpec.Condition == nil || inSpec.Condition.Operator != specifications.OP_IN { t.Error("IN spec not created correctly") } if ninSpec.Condition == nil || ninSpec.Condition.Operator != specifications.OP_NIN { t.Error("NIN spec not created correctly") } } func TestWhenUsingLogicalGroupHelpersThenCreatesCorrectSpecification(t *testing.T) { // Given andSpec := specifications.And(specifications.Eq("a", 1), specifications.Eq("b", 2)) orSpec := specifications.Or(specifications.Eq("a", 1), specifications.Eq("b", 2)) notSpec := specifications.Not(specifications.Eq("a", 1)) // When & Then if andSpec.LogicalGroup == nil || andSpec.LogicalGroup.Operator != specifications.GROUP_AND || len(andSpec.LogicalGroup.Conditions) != 2 { t.Error("AND spec not created correctly") } if orSpec.LogicalGroup == nil || orSpec.LogicalGroup.Operator != specifications.GROUP_OR || len(orSpec.LogicalGroup.Conditions) != 2 { t.Error("OR spec not created correctly") } if notSpec.LogicalGroup == nil || notSpec.LogicalGroup.Operator != specifications.GROUP_NOT || notSpec.LogicalGroup.Spec == nil { t.Error("NOT spec not created correctly") } } func TestGetSpecReturnsOriginalSpecification(t *testing.T) { // Given originalSpec := specifications.Eq("field", "value") specification := specifications.NewSimpleSpecification[*TestObject](originalSpec) // When retrievedSpec := specification.GetSpec() // Then if retrievedSpec != originalSpec { t.Error("Expected retrieved spec to be the same as original spec") } } func TestGetConditionsReturnsAllConditions(t *testing.T) { // Given spec := specifications.NewSimpleSpecification[*TestObject](specifications.And( specifications.Eq("status", "active"), specifications.Gte("score", 80), )) // When conditions := spec.GetConditions() // Then if len(conditions) != 2 { t.Error("Expected 2 conditions") } // Check that both conditions are present foundStatus := false foundScore := false for _, cond := range conditions { if cond.Field == "status" && cond.Operator == specifications.OP_EQ && cond.Value == "active" { foundStatus = true } if cond.Field == "score" && cond.Operator == specifications.OP_GTE && cond.Value == 80 { foundScore = true } } if !foundStatus { t.Error("Expected status condition to be found") } if !foundScore { t.Error("Expected score condition to be found") } } // Composite Specification Tests func TestCompositeSpecificationAndOperation(t *testing.T) { // Given leftSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("status", "active")) rightSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Gte("score", 80)) compositeSpec := leftSpec.And(rightSpec) matchingObject := NewTestObject(map[string]interface{}{ "status": "active", "score": 85, }) nonMatchingObject := NewTestObject(map[string]interface{}{ "status": "inactive", "score": 85, }) // When matchResult := compositeSpec.IsSatisfiedBy(matchingObject) noMatchResult := compositeSpec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the composite specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the composite specification") } } func TestCompositeSpecificationOrOperation(t *testing.T) { // Given leftSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("role", "admin")) rightSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Gte("score", 90)) compositeSpec := leftSpec.Or(rightSpec) matchingObject1 := NewTestObject(map[string]interface{}{ "role": "admin", "score": 70, }) matchingObject2 := NewTestObject(map[string]interface{}{ "role": "user", "score": 95, }) nonMatchingObject := NewTestObject(map[string]interface{}{ "role": "user", "score": 70, }) // When matchResult1 := compositeSpec.IsSatisfiedBy(matchingObject1) matchResult2 := compositeSpec.IsSatisfiedBy(matchingObject2) noMatchResult := compositeSpec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult1 { t.Error("Expected matching object 1 to satisfy the composite specification") } if !matchResult2 { t.Error("Expected matching object 2 to satisfy the composite specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the composite specification") } } func TestCompositeSpecificationNotOperation(t *testing.T) { // Given baseSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("status", "banned")) compositeSpec := baseSpec.Not() matchingObject := NewTestObject(map[string]interface{}{"status": "active"}) nonMatchingObject := NewTestObject(map[string]interface{}{"status": "banned"}) // When matchResult := compositeSpec.IsSatisfiedBy(matchingObject) noMatchResult := compositeSpec.IsSatisfiedBy(nonMatchingObject) // Then if !matchResult { t.Error("Expected matching object to satisfy the NOT specification") } if noMatchResult { t.Error("Expected non-matching object to not satisfy the NOT specification") } } func TestCompositeSpecificationGetConditions(t *testing.T) { // Given leftSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("status", "active")) rightSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Gte("score", 80)) compositeSpec := leftSpec.And(rightSpec) // When conditions := compositeSpec.GetConditions() // Then if len(conditions) != 2 { t.Error("Expected 2 conditions from composite specification") } // Check that both conditions are present foundStatus := false foundScore := false for _, cond := range conditions { if cond.Field == "status" && cond.Operator == specifications.OP_EQ && cond.Value == "active" { foundStatus = true } if cond.Field == "score" && cond.Operator == specifications.OP_GTE && cond.Value == 80 { foundScore = true } } if !foundStatus { t.Error("Expected status condition to be found") } if !foundScore { t.Error("Expected score condition to be found") } } func TestCompositeSpecificationGetSpecReturnsNil(t *testing.T) { // Given leftSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Eq("status", "active")) rightSpec := specifications.NewSimpleSpecification[*TestObject](specifications.Gte("score", 80)) compositeSpec := leftSpec.And(rightSpec) // When spec := compositeSpec.GetSpec() // Then if spec != nil { t.Error("Expected composite specification to return nil for GetSpec") } }