hako/internal/archival/rules/unmarshal_test.go

216 lines
5.6 KiB
Go

package rules
import (
"encoding/json"
"testing"
)
func TestUnmarshalRule(t *testing.T) {
tests := []struct {
name string
json string
wantErr bool
check func(t *testing.T, rule Rule)
}{
{
name: "mimetype rule",
json: `{"mimetype": "text/html", "extractors": [{"key": "obelisk"}]}`,
wantErr: false,
check: func(t *testing.T, rule Rule) {
mr, ok := rule.(*MimetypeRule)
if !ok {
t.Fatalf("expected MimetypeRule, got %T", rule)
}
if mr.Mimetype != "text/html" {
t.Errorf("Mimetype = %q, want %q", mr.Mimetype, "text/html")
}
if len(mr.Archivers) != 1 || mr.Archivers[0].Key != "obelisk" {
t.Errorf("Archivers = %v, want [{key: obelisk}]", mr.Archivers)
}
},
},
{
name: "hostname rule",
json: `{"hostname": "example.com", "extractors": [{"key": "direct_download"}]}`,
wantErr: false,
check: func(t *testing.T, rule Rule) {
hr, ok := rule.(*HostnameRule)
if !ok {
t.Fatalf("expected HostnameRule, got %T", rule)
}
if hr.Hostname != "example.com" {
t.Errorf("Hostname = %q, want %q", hr.Hostname, "example.com")
}
},
},
{
name: "and rule",
json: `{"and": [{"mimetype": "text/html"}, {"hostname": "example.com"}], "extractors": [{"key": "obelisk"}]}`,
wantErr: false,
check: func(t *testing.T, rule Rule) {
ar, ok := rule.(*AndRule)
if !ok {
t.Fatalf("expected AndRule, got %T", rule)
}
if len(ar.Rules) != 2 {
t.Errorf("Rules length = %d, want 2", len(ar.Rules))
}
},
},
{
name: "or rule",
json: `{"or": [{"mimetype": "text/html"}, {"hostname": "example.com"}], "extractors": [{"key": "obelisk"}]}`,
wantErr: false,
check: func(t *testing.T, rule Rule) {
or, ok := rule.(*OrRule)
if !ok {
t.Fatalf("expected OrRule, got %T", rule)
}
if len(or.Rules) != 2 {
t.Errorf("Rules length = %d, want 2", len(or.Rules))
}
},
},
{
name: "nested and/or rules",
json: `{"and": [{"or": [{"mimetype": "text/html"}, {"mimetype": "application/json"}]}, {"hostname": "example.com"}], "extractors": [{"key": "obelisk"}]}`,
wantErr: false,
check: func(t *testing.T, rule Rule) {
ar, ok := rule.(*AndRule)
if !ok {
t.Fatalf("expected AndRule, got %T", rule)
}
if len(ar.Rules) != 2 {
t.Errorf("Rules length = %d, want 2", len(ar.Rules))
}
or, ok := ar.Rules[0].(*OrRule)
if !ok {
t.Fatalf("expected first child to be OrRule, got %T", ar.Rules[0])
}
if len(or.Rules) != 2 {
t.Errorf("OrRule children length = %d, want 2", len(or.Rules))
}
},
},
{
name: "invalid rule - no type field",
json: `{"extractors": [{"key": "obelisk"}]}`,
wantErr: true,
},
{
name: "invalid json",
json: `{invalid json}`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rule, err := UnmarshalRule([]byte(tt.json))
if (err != nil) != tt.wantErr {
t.Errorf("UnmarshalRule() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
if rule == nil {
t.Fatal("UnmarshalRule() returned nil rule")
}
if tt.check != nil {
tt.check(t, rule)
}
})
}
}
func TestUnmarshalRuleConfig(t *testing.T) {
tests := []struct {
name string
json string
wantErr bool
check func(t *testing.T, config *RuleConfig)
}{
{
name: "valid config",
json: `{"rules": [{"mimetype": "text/html", "extractors": [{"key": "obelisk"}]}], "default_extractors": [{"key": "direct_download"}]}`,
wantErr: false,
check: func(t *testing.T, config *RuleConfig) {
if len(config.Rules) != 1 {
t.Errorf("Rules length = %d, want 1", len(config.Rules))
}
if len(config.DefaultArchivers) != 1 || config.DefaultArchivers[0].Key != "direct_download" {
t.Errorf("DefaultArchivers = %v, want [{key: direct_download}]", config.DefaultArchivers)
}
},
},
{
name: "empty rules",
json: `{"rules": [], "default_extractors": [{"key": "obelisk"}]}`,
wantErr: false,
check: func(t *testing.T, config *RuleConfig) {
if len(config.Rules) != 0 {
t.Errorf("Rules length = %d, want 0", len(config.Rules))
}
},
},
{
name: "invalid json",
json: `{invalid json}`,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config, err := UnmarshalRuleConfig([]byte(tt.json))
if (err != nil) != tt.wantErr {
t.Errorf("UnmarshalRuleConfig() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
if config == nil {
t.Fatal("UnmarshalRuleConfig() returned nil config")
}
if tt.check != nil {
tt.check(t, config)
}
})
}
}
func TestUnmarshalRule_RoundTrip(t *testing.T) {
// Test that we can marshal and unmarshal rules correctly
original := &MimetypeRule{
Mimetype: "application/*",
Archivers: []ArchiverConfig{
{Key: "direct_download", Config: map[string]any{"timeout": "5m"}},
},
}
// Marshal
data, err := json.Marshal(original)
if err != nil {
t.Fatalf("Marshal() error = %v", err)
}
// Unmarshal
unmarshaled, err := UnmarshalRule(data)
if err != nil {
t.Fatalf("UnmarshalRule() error = %v", err)
}
// Check
mr, ok := unmarshaled.(*MimetypeRule)
if !ok {
t.Fatalf("expected MimetypeRule, got %T", unmarshaled)
}
if mr.Mimetype != original.Mimetype {
t.Errorf("Mimetype = %q, want %q", mr.Mimetype, original.Mimetype)
}
if len(mr.Archivers) != len(original.Archivers) {
t.Errorf("Archivers length = %d, want %d", len(mr.Archivers), len(original.Archivers))
}
}