package steme import ( "testing" ) // TestCanonicalMessage tests the canonical message generation for signing. func TestCanonicalMessage(t *testing.T) { assertion := NewAssertion("Tesla_Inc", "has_revenue"). WithNumber(96.7). WithConfidence(0.95). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() msg1, err := canonicalAssertionMessage(&assertion) if err != nil { t.Fatalf("canonicalAssertionMessage() failed: %v", err) } // Verify format matches server expectation: "{subject}:{predicate}" expected := "Tesla_Inc:has_revenue" if string(msg1) != expected { t.Errorf("canonicalAssertionMessage() = %q, want %q", string(msg1), expected) } // Same assertion should produce same message msg2, err := canonicalAssertionMessage(&assertion) if err != nil { t.Fatalf("canonicalAssertionMessage() failed: %v", err) } if string(msg1) != string(msg2) { t.Errorf("Canonical message not deterministic") } // Different subject/predicate should produce different message assertion2 := NewAssertion("Tesla_Inc", "has_employees"). // Different predicate WithNumber(97.0). WithConfidence(0.95). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() msg3, err := canonicalAssertionMessage(&assertion2) if err != nil { t.Fatalf("canonicalAssertionMessage() failed: %v", err) } if string(msg1) == string(msg3) { t.Errorf("Different assertions produced same canonical message") } } // TestCanonicalMessageAllObjectTypes tests canonical message with all object types. // The canonical message is just "{subject}:{predicate}" - object type doesn't affect it. func TestCanonicalMessageAllObjectTypes(t *testing.T) { types := []struct { name string obj ObjectValue }{ {"text", NewTextValue("hello")}, {"number", NewNumberValue(42.5)}, {"boolean", NewBooleanValue(true)}, {"reference", NewReferenceValue("Entity_123")}, } for _, tt := range types { t.Run(tt.name, func(t *testing.T) { a := NewAssertion("Subject", "Predicate"). WithConfidence(0.5). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() a.Object = tt.obj msg, err := canonicalAssertionMessage(&a) if err != nil { t.Errorf("canonicalAssertionMessage() failed for %s: %v", tt.name, err) } // Canonical message is "{subject}:{predicate}" regardless of object type expected := "Subject:Predicate" if string(msg) != expected { t.Errorf("Expected %q, got %q", expected, string(msg)) } }) } } // TestCanonicalMessageEdgeCases tests edge cases in canonical message generation. func TestCanonicalMessageEdgeCases(t *testing.T) { tests := []struct { name string build func() Assertion wantErr bool expected string }{ { name: "zero confidence", build: func() Assertion { return NewAssertion("S", "P"). WithText("v"). WithConfidence(0.0). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() }, wantErr: false, expected: "S:P", }, { name: "max confidence", build: func() Assertion { return NewAssertion("S", "P"). WithText("v"). WithConfidence(1.0). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() }, wantErr: false, expected: "S:P", }, { name: "empty strings", build: func() Assertion { return NewAssertion("", ""). WithText("v"). WithConfidence(0.5). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() }, wantErr: false, expected: ":", }, { name: "special characters", build: func() Assertion { return NewAssertion("Subject:With:Colons", "Predicate"). WithText("v"). WithConfidence(0.5). WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000"). Build() }, wantErr: false, expected: "Subject:With:Colons:Predicate", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := tt.build() msg, err := canonicalAssertionMessage(&a) if (err != nil) != tt.wantErr { t.Errorf("error = %v, wantErr %v", err, tt.wantErr) } if !tt.wantErr && string(msg) != tt.expected { t.Errorf("got %q, want %q", string(msg), tt.expected) } }) } }