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.
138 lines
4.7 KiB
138 lines
4.7 KiB
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)
|
|
}
|
|
}
|
|
|
|
func TestFactoryResetSocketErasesProtectedLogs(t *testing.T) {
|
|
store, err := logstorage.Open(logstorage.Options{Root: t.TempDir(), FreeBytes: func(string) (int64, error) { return 1 << 40, nil }})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(t.TempDir(), "reset.sock")
|
|
listener, err := net.Listen("unix", path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
done := make(chan error, 1)
|
|
go func() { done <- (Server{Store: store}).Serve(listener) }()
|
|
defer func() { listener.Close(); <-done }()
|
|
client := NewClient(path)
|
|
for _, class := range []string{logstorage.ClassAudit, logstorage.ClassSystem, logstorage.ClassPort, logstorage.ClassLink} {
|
|
if err := client.Append(class, time.Now(), map[string]string{"message": "old"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := client.FactoryReset(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, class := range []string{logstorage.ClassAudit, logstorage.ClassSystem, logstorage.ClassPort, logstorage.ClassLink} {
|
|
items, err := client.Read(class)
|
|
if err != nil || len(items) != 0 {
|
|
t.Fatal("log survived socket reset")
|
|
}
|
|
}
|
|
}
|
|
|