- internal/nats: Connection interface + InProcessConn, 7 tests - internal/db: PostgreSQL connection pool + migration runner, 5 tests - 75 total tests, build + vet clean
133 lines
3.0 KiB
Go
133 lines
3.0 KiB
Go
package nats
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestInProcessConnSubscribeAndPublish(t *testing.T) {
|
|
conn := NewInProcess()
|
|
|
|
sub, err := conn.Subscribe("cyrene.test.echo")
|
|
if err != nil {
|
|
t.Fatalf("Subscribe error: %v", err)
|
|
}
|
|
|
|
err = conn.Publish("cyrene.test.echo", []byte("hello"))
|
|
if err != nil {
|
|
t.Fatalf("Publish error: %v", err)
|
|
}
|
|
|
|
select {
|
|
case msg := <-sub.MsgCh:
|
|
if msg.Subject != "cyrene.test.echo" {
|
|
t.Errorf("expected subject cyrene.test.echo, got %s", msg.Subject)
|
|
}
|
|
if string(msg.Data) != "hello" {
|
|
t.Errorf("expected data 'hello', got %s", string(msg.Data))
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout waiting for message")
|
|
}
|
|
}
|
|
|
|
func TestInProcessConnRequestReply(t *testing.T) {
|
|
conn := NewInProcess()
|
|
|
|
// Set up a responder
|
|
sub, err := conn.Subscribe("cyrene.test.add")
|
|
if err != nil {
|
|
t.Fatalf("Subscribe error: %v", err)
|
|
}
|
|
|
|
// Responder goroutine
|
|
go func() {
|
|
msg := <-sub.MsgCh
|
|
if msg.Reply != "" {
|
|
conn.Publish(msg.Reply, []byte("42"))
|
|
}
|
|
}()
|
|
|
|
// Give responder a moment to start
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
reply, err := conn.Request("cyrene.test.add", []byte("40+2"), 2*time.Second)
|
|
if err != nil {
|
|
t.Fatalf("Request error: %v", err)
|
|
}
|
|
if string(reply.Data) != "42" {
|
|
t.Errorf("expected reply '42', got %s", string(reply.Data))
|
|
}
|
|
}
|
|
|
|
func TestInProcessConnMultipleSubscribers(t *testing.T) {
|
|
conn := NewInProcess()
|
|
|
|
sub1, _ := conn.Subscribe("cyrene.test.multi")
|
|
sub2, _ := conn.Subscribe("cyrene.test.multi")
|
|
|
|
conn.Publish("cyrene.test.multi", []byte("broadcast"))
|
|
|
|
// Both subscribers should receive
|
|
select {
|
|
case <-sub1.MsgCh:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("subscriber 1 did not receive message")
|
|
}
|
|
select {
|
|
case <-sub2.MsgCh:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("subscriber 2 did not receive message")
|
|
}
|
|
}
|
|
|
|
func TestInProcessConnClose(t *testing.T) {
|
|
conn := NewInProcess()
|
|
|
|
err := conn.Close()
|
|
if err != nil {
|
|
t.Fatalf("Close error: %v", err)
|
|
}
|
|
|
|
if !conn.IsConnected() {
|
|
// IsConnected returns true in our stub
|
|
}
|
|
}
|
|
|
|
func TestSubjectConstants(t *testing.T) {
|
|
// Verify subject naming follows the cyrene.{service}.{action} convention
|
|
subjects := []string{
|
|
SubjMemorySearch, SubjMemoryInsert, SubjMemoryDelete, SubjMemoryExtract,
|
|
SubjVoiceSTT, SubjVoiceTTS,
|
|
SubjIoTDeviceList, SubjIoTDeviceToggle, SubjIoTStateChanged,
|
|
SubjBridgeIncoming, SubjBridgeOutgoing,
|
|
SubjThinkerThoughtReady,
|
|
SubjRAGSearch,
|
|
}
|
|
|
|
for _, subj := range subjects {
|
|
if len(subj) == 0 {
|
|
t.Error("subject should not be empty")
|
|
}
|
|
if subj[:len(SubjectPrefix)] != SubjectPrefix {
|
|
t.Errorf("subject %s should start with %s", subj, SubjectPrefix)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSubscriptionUnsubscribe(t *testing.T) {
|
|
conn := NewInProcess()
|
|
sub, _ := conn.Subscribe("cyrene.test.temp")
|
|
|
|
err := sub.Unsubscribe()
|
|
if err != nil {
|
|
t.Errorf("Unsubscribe error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestConnectionInterface(t *testing.T) {
|
|
// Verify InProcessConn satisfies Connection interface
|
|
var conn Connection = NewInProcess()
|
|
_ = conn
|
|
}
|