ultisuite-backend/internal/migration/job_audit_export_test.go
R3D347HR4Y 1ffd0817d8
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(migration): enhance migration API with roster and audit export features
- Added endpoints for listing and importing migration rosters.
- Introduced audit export functionality for migration jobs in CSV and NDJSON formats.
- Implemented tenant mismatch validation for Microsoft migration claims.
- Enhanced error handling for email claiming and migration processes.
- Added integration tests for roster import and claim workflows.
2026-06-13 13:11:30 +02:00

116 lines
3.0 KiB
Go

package migration
import (
"bytes"
"encoding/csv"
"encoding/json"
"strings"
"testing"
)
func TestJobAuditExportCSVFormat(t *testing.T) {
var buf bytes.Buffer
cw := csv.NewWriter(&buf)
if err := cw.Write(jobAuditCSVHeaders); err != nil {
t.Fatal(err)
}
row := JobAuditExportRow{
ItemID: "msg-fail",
RelPath: "Inbox/foo.eml",
Status: ItemStatusFailed,
Error: "upload timeout",
Service: "mail",
Timestamp: "2026-06-13T12:00:00Z",
}
if err := writeJobAuditCSVRow(cw, row); err != nil {
t.Fatal(err)
}
cw.Flush()
if err := cw.Error(); err != nil {
t.Fatal(err)
}
reader := csv.NewReader(strings.NewReader(buf.String()))
records, err := reader.ReadAll()
if err != nil {
t.Fatal(err)
}
if len(records) != 2 {
t.Fatalf("records = %d, want 2", len(records))
}
if got := strings.Join(records[0], ","); got != "item_id,rel_path,status,error,service,timestamp" {
t.Fatalf("headers = %q", got)
}
if records[1][0] != "msg-fail" || records[1][2] != ItemStatusFailed || records[1][3] != "upload timeout" {
t.Fatalf("row = %#v", records[1])
}
}
func TestProjectAuditExportCSVFormat(t *testing.T) {
var buf bytes.Buffer
cw := csv.NewWriter(&buf)
if err := cw.Write(projectAuditCSVHeaders); err != nil {
t.Fatal(err)
}
if err := writeProjectAuditCSVRow(cw, JobAuditExportRow{
JobID: "job-1",
ItemID: "file-1",
Status: ItemStatusImported,
Service: "drive",
Timestamp: "2026-06-13T12:00:00Z",
}); err != nil {
t.Fatal(err)
}
cw.Flush()
reader := csv.NewReader(strings.NewReader(buf.String()))
records, err := reader.ReadAll()
if err != nil {
t.Fatal(err)
}
if records[0][0] != "job_id" || records[1][0] != "job-1" {
t.Fatalf("records = %#v", records)
}
}
func TestJobAuditExportNDJSONFormat(t *testing.T) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if err := enc.Encode(JobAuditExportRow{
ItemID: "msg-skip",
Status: ItemStatusSkipped,
Error: "file too large",
Service: "mail",
Timestamp: "2026-06-13T12:00:00Z",
}); err != nil {
t.Fatal(err)
}
line := strings.TrimSpace(buf.String())
var decoded JobAuditExportRow
if err := json.Unmarshal([]byte(line), &decoded); err != nil {
t.Fatal(err)
}
if decoded.ItemID != "msg-skip" || decoded.Status != ItemStatusSkipped || decoded.Error != "file too large" {
t.Fatalf("decoded = %#v", decoded)
}
}
func TestJobAuditExportMeta(t *testing.T) {
csvMeta := jobAuditExportMeta("csv", "01234567-abcd-efgh", false)
if csvMeta.ContentType != "text/csv; charset=utf-8" {
t.Fatalf("csv content type = %q", csvMeta.ContentType)
}
if !strings.HasSuffix(csvMeta.FileName, ".csv") {
t.Fatalf("csv filename = %q", csvMeta.FileName)
}
ndMeta := jobAuditExportMeta("ndjson", "01234567-abcd-efgh", true)
if ndMeta.ContentType != "application/x-ndjson; charset=utf-8" {
t.Fatalf("ndjson content type = %q", ndMeta.ContentType)
}
if !strings.HasPrefix(ndMeta.FileName, "migration-project-audit-") {
t.Fatalf("project filename = %q", ndMeta.FileName)
}
}