You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

85 lines
3.2 KiB

package mock
import (
"context"
"strings"
"testing"
"time"
"tera-clone/services/tera-api/internal/directstream"
"tera-clone/services/tera-api/internal/domain"
)
func TestDirectTicketAuthorityConsumesAuditsAndAppliesVerifiedLifecycle(t *testing.T) {
store := NewStore(time.Now)
ctx := domain.WithActor(context.Background(), domain.Actor{Name: "admin"})
session, err := store.OpenDirectSession(ctx, "CH01", domain.DirectSessionRequest{Access: directstream.AccessWriter})
if err != nil {
t.Fatalf("open direct session: %v", err)
}
authority := NewDirectTicketAuthority(store)
claims, err := authority.ConsumeTicket(context.Background(), session.StreamTicket, directstream.TicketBinding{
SessionID: session.SessionID, Channel: session.Channel,
})
if err != nil {
t.Fatalf("consume ticket: %v", err)
}
if claims.Actor != "admin" || claims.Access != directstream.AccessWriter {
t.Fatalf("claims = %#v", claims)
}
store.mu.Lock()
store.runningRetention.LocalOperationEnabled = false
store.mu.Unlock()
connected := directstream.SessionEvent{
Type: directstream.SessionEventConnected, Source: "192.168.0.10",
Timestamp: time.Date(2026, 8, 13, 3, 4, 5, 0, time.UTC),
}
if err := authority.RecordConsumedStreamEvent(context.Background(), claims, connected); err != nil {
t.Fatalf("record connected stream event: %v", err)
}
disconnected := directstream.SessionEvent{
Type: directstream.SessionEventDisconnected, Source: connected.Source,
Timestamp: connected.Timestamp.Add(time.Minute),
}
if err := authority.RecordConsumedStreamEvent(context.Background(), claims, disconnected); err != nil {
t.Fatalf("record disconnected stream event: %v", err)
}
if err := authority.ReleaseConsumedWriter(context.Background(), claims); err != nil {
t.Fatalf("release writer: %v", err)
}
if err := authority.CloseConsumedStream(context.Background(), claims); err != nil {
t.Fatalf("close stream: %v", err)
}
events, err := store.AuditEvents(context.Background())
if err != nil {
t.Fatal(err)
}
correlated := map[string]bool{
"direct.session.open": false,
"direct.stream.ticket.consume": false,
"direct.writer.release": false,
"direct.session.close": false,
}
for _, event := range events {
if _, required := correlated[event.Action]; required && event.Actor == "admin" && event.Result == "succeeded" && strings.Contains(event.Message, session.SessionID) {
correlated[event.Action] = true
}
}
for action, found := range correlated {
if !found {
t.Fatalf("%s audit is not correlated by session %s: %#v", action, session.SessionID, events)
}
}
portEvents, err := store.PortLogEvents(context.Background(), domain.PortLogFilter{Channel: "CH01", EventType: "session"})
if err != nil {
t.Fatal(err)
}
wantWelcomeLog := strings.Join(strings.Fields(directstream.SessionEventMessage(claims, connected)), " ")
if len(portEvents) != 2 || portEvents[0].Actor != "admin/"+session.SessionID || portEvents[0].Message != wantWelcomeLog {
t.Fatalf("port session events = %#v", portEvents)
}
if !strings.Contains(portEvents[1].Message, "session disconnected") || !strings.Contains(portEvents[1].Message, "Serial channel: CH01") {
t.Fatalf("disconnect port event = %#v", portEvents[1])
}
}