d9d0beda3b
94 new tests across 4 packages. Coverage: engine 8.3%→65.1%, transport
26.3%→84.6%, proxmox 5.1%→82.7%, audit 0%→100%. Bug fix: dispatch.go
bytesReadCloser.Read returned fmt.Errorf("EOF") instead of io.EOF —
broke HTTP request body transmission (latent since v0.2 P02).
---ci---
project: orca
phase: 3
milestone: v0.7
status: verify
requirements:
covered: [REQ-055]
partial: []
---/ci---
141 lines
4.0 KiB
Go
141 lines
4.0 KiB
Go
package audit
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"log/slog"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.cloudinit.dev/coreci/orca/internal/engine"
|
|
"git.cloudinit.dev/coreci/orca/internal/store"
|
|
)
|
|
|
|
func newTestAudit(t *testing.T) (*Audit, *store.AuditRepo, func()) {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "test.db")
|
|
db, err := store.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("open db: %v", err)
|
|
}
|
|
repo := store.NewAuditRepo(db)
|
|
eng := engine.NewAudit(repo, nil)
|
|
return New(eng), repo, func() { _ = db.Close() }
|
|
}
|
|
|
|
func TestAudit_Emit(t *testing.T) {
|
|
a, repo, cleanup := newTestAudit(t)
|
|
defer cleanup()
|
|
ctx := context.Background()
|
|
a.Emit(ctx, ActionCertIssued, "cert:node-1", ResultSuccess, map[string]any{"cn": "node-1"})
|
|
|
|
entries, err := repo.List(ctx, 10)
|
|
if err != nil {
|
|
t.Fatalf("List: %v", err)
|
|
}
|
|
if len(entries) != 1 {
|
|
t.Fatalf("expected 1 audit entry, got %d", len(entries))
|
|
}
|
|
e := entries[0]
|
|
if e.Action != string(ActionCertIssued) {
|
|
t.Errorf("action: got %q, want %q", e.Action, ActionCertIssued)
|
|
}
|
|
if e.Result != string(ResultSuccess) {
|
|
t.Errorf("result: got %q, want %q", e.Result, ResultSuccess)
|
|
}
|
|
if e.Resource != "cert:node-1" {
|
|
t.Errorf("resource: got %q, want cert:node-1", e.Resource)
|
|
}
|
|
if e.Actor != "security" {
|
|
t.Errorf("actor: got %q, want security", e.Actor)
|
|
}
|
|
if e.Error != "" {
|
|
t.Errorf("error: got %q, want empty", e.Error)
|
|
}
|
|
}
|
|
|
|
func TestAudit_EmitWithErr(t *testing.T) {
|
|
a, repo, cleanup := newTestAudit(t)
|
|
defer cleanup()
|
|
ctx := context.Background()
|
|
a.EmitWithErr(ctx, ActionNodeHandshakeFail, "hs:node-2", errors.New("bad cert"), nil)
|
|
|
|
entries, err := repo.List(ctx, 10)
|
|
if err != nil {
|
|
t.Fatalf("List: %v", err)
|
|
}
|
|
if len(entries) != 1 {
|
|
t.Fatalf("expected 1 audit entry, got %d", len(entries))
|
|
}
|
|
e := entries[0]
|
|
if e.Result != string(ResultFailure) {
|
|
t.Errorf("result: got %q, want %q", e.Result, ResultFailure)
|
|
}
|
|
if !strings.Contains(e.Error, "bad cert") {
|
|
t.Errorf("error: got %q, want it to contain 'bad cert'", e.Error)
|
|
}
|
|
}
|
|
|
|
func TestAudit_LogHandshakeOK(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
logger := slog.New(slog.NewTextHandler(&buf, nil))
|
|
LogHandshakeOK(logger, "peer-1", "AA:BB:CC")
|
|
out := buf.String()
|
|
for _, want := range []string{"event=mtls.handshake", "result=ok", "peer=peer-1", "cert_fp=AA:BB:CC"} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("LogHandshakeOK: output missing %q\noutput: %s", want, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAudit_LogHandshakeFailed(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
logger := slog.New(slog.NewTextHandler(&buf, nil))
|
|
LogHandshakeFailed(logger, "peer-2", "", errors.New("tls: handshake"))
|
|
out := buf.String()
|
|
for _, want := range []string{"event=mtls.handshake", "result=failed", "peer=peer-2", "err=\"tls: handshake\""} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("LogHandshakeFailed: output missing %q\noutput: %s", want, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAudit_LogHandshake_NilLogger(t *testing.T) {
|
|
LogHandshakeOK(nil, "p", "fp")
|
|
LogHandshakeFailed(nil, "p", "fp", errors.New("x"))
|
|
}
|
|
|
|
func TestAudit_NilSafe(t *testing.T) {
|
|
var a *Audit
|
|
a.Emit(context.Background(), ActionCertIssued, "x", ResultSuccess, nil)
|
|
a.EmitWithErr(context.Background(), ActionCertIssued, "x", errors.New("y"), nil)
|
|
}
|
|
|
|
func TestAction_String(t *testing.T) {
|
|
if got := ActionCertIssued.String(); got != "cert.issued" {
|
|
t.Errorf("ActionCertIssued.String(): got %q, want cert.issued", got)
|
|
}
|
|
if got := ActionNodeHandshakeOK.String(); got != "node.handshake_ok" {
|
|
t.Errorf("ActionNodeHandshakeOK.String(): got %q, want node.handshake_ok", got)
|
|
}
|
|
}
|
|
|
|
func TestResult_String(t *testing.T) {
|
|
if got := ResultSuccess.String(); got != "success" {
|
|
t.Errorf("ResultSuccess.String(): got %q, want success", got)
|
|
}
|
|
if got := ResultFailure.String(); got != "failure" {
|
|
t.Errorf("ResultFailure.String(): got %q, want failure", got)
|
|
}
|
|
}
|
|
|
|
func TestFormatAction(t *testing.T) {
|
|
got := FormatAction(ActionCertIssued, ResultSuccess)
|
|
want := "action=cert.issued result=success"
|
|
if got != want {
|
|
t.Errorf("FormatAction: got %q, want %q", got, want)
|
|
}
|
|
}
|