package logservice import ( "errors" "net" "os" "path/filepath" "testing" "time" "tera-clone/services/tera-api/internal/logstorage" ) func TestClientServerRoundTripAndRetention(t *testing.T) { root := t.TempDir() now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) store, err := logstorage.Open(logstorage.Options{ Root: root, SegmentBytes: 1 << 20, Clock: func() time.Time { return now }, FreeBytes: func(string) (int64, error) { return 1 << 40, nil }, }) if err != nil { t.Fatal(err) } socketPath := filepath.Join(t.TempDir(), "log.sock") listener, err := net.Listen("unix", socketPath) if err != nil { if errors.Is(err, os.ErrPermission) { t.Skipf("Unix sockets unavailable: %v", err) } t.Fatal(err) } defer listener.Close() done := make(chan error, 1) go func() { done <- (Server{Store: store}).Serve(listener) }() defer func() { _ = listener.Close() <-done }() client := NewClient(socketPath) for _, channel := range []string{"CH01", "CH02"} { if err := client.Append(logstorage.ClassPort, now, map[string]string{"channel": channel}); err != nil { t.Fatal(err) } } if _, err := client.call(command{Action: "clear_port", Class: logstorage.ClassAudit, Channel: "CH01"}); err == nil { t.Fatal("protected class deletion accepted") } if n, err := client.ClearPort("CH01"); err != nil || n != 1 { t.Fatalf("clear socket = %d, %v", n, err) } if revision, err := client.PortClearRevision(); err != nil || revision != 1 { t.Fatalf("clear revision = %d, %v", revision, err) } if rows, err := client.Read(logstorage.ClassPort); err != nil || len(rows) != 1 { t.Fatalf("clear survivors = %d, %v", len(rows), err) } sequence, err := client.AppendSequence(logstorage.ClassSystem, now.Add(-48*time.Hour), map[string]string{"message": "expired"}) if err != nil { t.Fatalf("Append: %v", err) } if sequence != 1 { t.Fatalf("Append sequence = %d, want 1", sequence) } after, err := client.ReadAfter(logstorage.ClassSystem, sequence) if err != nil || len(after) != 0 { t.Fatalf("ReadAfter = %d, %v", len(after), err) } records, err := client.Read(logstorage.ClassSystem) if err != nil || len(records) != 1 { t.Fatalf("Read = %d, %v", len(records), err) } if err := client.ApplyRetention(logstorage.Retention{AuditDays: 365, SystemDays: 1, PortDays: 365}); err != nil { t.Fatalf("ApplyRetention: %v", err) } records, err = client.Read(logstorage.ClassSystem) if err != nil || len(records) != 0 { t.Fatalf("Read after retention = %d, %v", len(records), err) } snapshot, err := client.Snapshot() for _, class := range []string{logstorage.ClassAudit, logstorage.ClassPort, logstorage.ClassLink, ""} { if _, err := client.call(command{Action: "clear_system", Class: class}); err == nil { t.Fatalf("accepted protected class %s", class) } } if err := client.Append(logstorage.ClassSystem, now, map[string]string{"message": "delete"}); err != nil { t.Fatal(err) } if n, err := client.ClearSystem(); err != nil || n != 1 { t.Fatalf("system clear=%d,%v", n, err) } if revision, err := client.SystemClearRevision(); err != nil || revision != 1 { t.Fatalf("system revision=%d,%v", revision, err) } if rows, err := client.Read(logstorage.ClassPort); err != nil || len(rows) != 1 { t.Fatal("port log affected", err) } if err != nil || snapshot.Root != root || snapshot.GlobalFreeFloor != 0 { t.Fatalf("Snapshot = %+v, %v", snapshot, err) } } func TestClientReportsUnavailableSocket(t *testing.T) { client := NewClient(filepath.Join(t.TempDir(), "missing.sock")) if _, err := client.Read(logstorage.ClassAudit); !errors.Is(err, ErrUnavailable) { t.Fatalf("Read error = %v, want unavailable", err) } }