a73x

5096042b

feat: add Match and SaveResponse to middleware

a73x   2026-03-31 05:47

Commit message
feat: add Match and SaveResponse to middleware

middleware/middleware.go
Old New
@@ -57,3 +57,21 @@ func New(path string) (*Middleware, error) {
57 func (m *Middleware) RuleCount() int { 57 func (m *Middleware) RuleCount() int {
58 return len(m.rules) 58 return len(m.rules)
59 } 59 }
60
61 // Match checks if host+path matches any middleware rule.
62 // Returns the first matching rule, or nil.
63 func (m *Middleware) Match(host, path string) *Rule {
64 url := host + path
65 for i := range m.rules {
66 if m.rules[i].Match == url {
67 return &m.rules[i]
68 }
69 }
70 return nil
71 }
72
73 // SaveResponse writes the response body to the rule's destination file,
74 // overwriting any existing content.
75 func (r *Rule) SaveResponse(body []byte) error {
76 return os.WriteFile(r.Dest, body, 0644)
77 }
middleware/middleware_test.go
Old New
@@ -50,3 +50,80 @@ func TestNewRejectsUnknownAction(t *testing.T) {
50 t.Fatal("expected error for unknown action") 50 t.Fatal("expected error for unknown action")
51 } 51 }
52 } 52 }
53
54 func TestMatchReturnsRuleForMatchingURL(t *testing.T) {
55 dir := t.TempDir()
56 path := filepath.Join(dir, "middleware.yaml")
57 os.WriteFile(path, []byte(`middleware:
58 - match: "api.anthropic.com/api/oauth/usage"
59 action: save_response
60 dest: "/tmp/usage.json"
61 `), 0644)
62
63 mw, _ := middleware.New(path)
64 rule := mw.Match("api.anthropic.com", "/api/oauth/usage")
65 if rule == nil {
66 t.Fatal("expected a match")
67 }
68 if rule.Dest != "/tmp/usage.json" {
69 t.Errorf("expected dest /tmp/usage.json, got %s", rule.Dest)
70 }
71 }
72
73 func TestMatchReturnsNilForNoMatch(t *testing.T) {
74 dir := t.TempDir()
75 path := filepath.Join(dir, "middleware.yaml")
76 os.WriteFile(path, []byte(`middleware:
77 - match: "api.anthropic.com/api/oauth/usage"
78 action: save_response
79 dest: "/tmp/usage.json"
80 `), 0644)
81
82 mw, _ := middleware.New(path)
83 rule := mw.Match("example.com", "/other")
84 if rule != nil {
85 t.Fatal("expected no match")
86 }
87 }
88
89 func TestSaveResponseWritesBodyToFile(t *testing.T) {
90 dest := filepath.Join(t.TempDir(), "out.json")
91 rule := &middleware.Rule{
92 Match: "example.com/data",
93 Action: "save_response",
94 Dest: dest,
95 }
96
97 body := []byte(`{"tokens": 42}`)
98 err := rule.SaveResponse(body)
99 if err != nil {
100 t.Fatalf("unexpected error: %v", err)
101 }
102
103 got, err := os.ReadFile(dest)
104 if err != nil {
105 t.Fatalf("failed to read dest file: %v", err)
106 }
107 if string(got) != string(body) {
108 t.Errorf("expected %q, got %q", body, got)
109 }
110 }
111
112 func TestSaveResponseOverwritesExistingFile(t *testing.T) {
113 dest := filepath.Join(t.TempDir(), "out.json")
114 os.WriteFile(dest, []byte("old data"), 0644)
115
116 rule := &middleware.Rule{
117 Match: "example.com/data",
118 Action: "save_response",
119 Dest: dest,
120 }
121
122 body := []byte(`{"new": true}`)
123 rule.SaveResponse(body)
124
125 got, _ := os.ReadFile(dest)
126 if string(got) != string(body) {
127 t.Errorf("expected %q, got %q", body, got)
128 }
129 }