Merge PR #2: Phase 1 Foundation
Reviewed and merged Phase 1 Foundation PR. All tests pass, CI changes look good, linting config is comprehensive, and shared test utilities improve code quality. The merge preserves the existing CLI on main.
This commit is contained in:
commit
fbcb5a6dbd
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@ -17,6 +17,14 @@ jobs:
|
||||
go-version: '1.25'
|
||||
cache: true
|
||||
|
||||
- name: Check formatting
|
||||
run: |
|
||||
if [ -n "$(gofmt -l .)" ]; then
|
||||
echo "Files not formatted:"
|
||||
gofmt -l .
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
@ -41,6 +49,21 @@ jobs:
|
||||
curl -fsS http://localhost:8080/healthz | grep -q 'ok'
|
||||
docker stop hatch-ci
|
||||
|
||||
lint-go:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25'
|
||||
cache: true
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
|
||||
lint-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -12,6 +12,11 @@ go.work
|
||||
/bin
|
||||
/dist
|
||||
|
||||
# Runtime data
|
||||
data/
|
||||
*.db
|
||||
coverage.out
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
57
.golangci.yml
Normal file
57
.golangci.yml
Normal file
@ -0,0 +1,57 @@
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- gofmt
|
||||
- misspell
|
||||
- unconvert
|
||||
- unparam
|
||||
- prealloc
|
||||
- gocritic
|
||||
- revive
|
||||
|
||||
linters-settings:
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
govet:
|
||||
check-shadowing: true
|
||||
revive:
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: indent-error-flow
|
||||
- name: superfluous-else
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on test files
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- gocritic
|
||||
- unparam
|
||||
# Exclude known issues in test files
|
||||
- path: _test\.go
|
||||
text: "func name"
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
modules-download-mode: readonly
|
||||
34
Makefile
Normal file
34
Makefile
Normal file
@ -0,0 +1,34 @@
|
||||
.PHONY: all build test lint fmt vet clean help
|
||||
|
||||
all: lint test build
|
||||
|
||||
## build: Build the hatch binary
|
||||
build:
|
||||
CGO_ENABLED=0 go build -o hatch ./cmd/hatch
|
||||
|
||||
## test: Run all tests with race detection and coverage
|
||||
test:
|
||||
go test ./... -v -race -coverprofile=coverage.out
|
||||
|
||||
## lint: Run golangci-lint
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
## fmt: Format code with gofmt
|
||||
fmt:
|
||||
gofmt -s -w .
|
||||
|
||||
## vet: Run go vet
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
## clean: Remove build artifacts
|
||||
clean:
|
||||
rm -f hatch coverage.out
|
||||
|
||||
## help: Show this help message
|
||||
help:
|
||||
@echo "Usage: make [target]"
|
||||
@echo ""
|
||||
@echo "Targets:"
|
||||
@grep -E '^## ' $(MAKEFILE_LIST) | sed 's/## / /'
|
||||
@ -8,10 +8,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/elfoundation/hatch/internal/store"
|
||||
"github.com/elfoundation/hatch/internal/testutil"
|
||||
)
|
||||
|
||||
func TestV1CaptureRequest(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
r := testRouter(repo)
|
||||
|
||||
body := `{"method":"POST","path":"/test","headers":{"X-Custom":"value"},"query":"foo=bar","body":"hello"}`
|
||||
@ -35,16 +36,16 @@ func TestV1CaptureRequest(t *testing.T) {
|
||||
t.Errorf("expected path /test, got %v", resp["path"])
|
||||
}
|
||||
// Verify request stored in repo.
|
||||
if len(repo.requests) != 1 {
|
||||
t.Fatalf("expected 1 request stored, got %d", len(repo.requests))
|
||||
if len(repo.Requests) != 1 {
|
||||
t.Fatalf("expected 1 request stored, got %d", len(repo.Requests))
|
||||
}
|
||||
if repo.requests[0].Headers != `{"X-Custom":"value"}` {
|
||||
t.Errorf("expected headers JSON, got %q", repo.requests[0].Headers)
|
||||
if repo.Requests[0].Headers != `{"X-Custom":"value"}` {
|
||||
t.Errorf("expected headers JSON, got %q", repo.Requests[0].Headers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestV1ListRequests(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "list-ep")
|
||||
// Add a few requests.
|
||||
repo.AppendRequest(nil, "list-ep", &store.Request{Method: "GET", Path: "/a"})
|
||||
@ -77,7 +78,7 @@ func TestV1ListRequests(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestV1SearchRequests(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "search-ep")
|
||||
repo.AppendRequest(nil, "search-ep", &store.Request{Method: "GET", Path: "/users", Headers: `{"Accept":"json"}`})
|
||||
repo.AppendRequest(nil, "search-ep", &store.Request{Method: "POST", Path: "/orders", Body: []byte(`{"item":"apple"}`)})
|
||||
@ -111,7 +112,7 @@ func TestV1SearchRequests(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestV1MockSet(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "mock-ep")
|
||||
r := testRouter(repo)
|
||||
|
||||
@ -138,7 +139,7 @@ func TestV1MockSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestV1OpenAPI(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
r := testRouter(repo)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/endpoints/test/openapi.json", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
@ -11,89 +11,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/elfoundation/hatch/internal/store"
|
||||
"github.com/elfoundation/hatch/internal/testutil"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
// fakeRepo implements store.Repository for tests.
|
||||
type fakeRepo struct {
|
||||
endpoints map[string]*store.Endpoint
|
||||
requests []*store.Request
|
||||
mocks map[string]*store.MockConfig
|
||||
}
|
||||
|
||||
func newFakeRepo() *fakeRepo {
|
||||
return &fakeRepo{
|
||||
endpoints: map[string]*store.Endpoint{},
|
||||
mocks: map[string]*store.MockConfig{},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeRepo) CreateEndpoint(_ context.Context, u string) (*store.Endpoint, error) {
|
||||
e := &store.Endpoint{ID: u, URL: u, CreatedAt: "t", UpdatedAt: "t"}
|
||||
f.endpoints[u] = e
|
||||
return e, nil
|
||||
}
|
||||
func (f *fakeRepo) GetEndpoint(_ context.Context, id string) (*store.Endpoint, error) {
|
||||
e, ok := f.endpoints[id]
|
||||
if !ok {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
func (f *fakeRepo) AppendRequest(_ context.Context, eid string, r *store.Request) error {
|
||||
r.ID = "req-" + string(rune(len(f.requests)+'0'))
|
||||
r.EndpointID = eid
|
||||
f.requests = append(f.requests, r)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeRepo) GetRequest(_ context.Context, id string) (*store.Request, error) {
|
||||
for _, r := range f.requests {
|
||||
if r.ID == id {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return nil, errNotFound
|
||||
}
|
||||
func (f *fakeRepo) ListRequests(_ context.Context, _ string, _ int) ([]*store.Request, error) {
|
||||
return f.requests, nil
|
||||
}
|
||||
func (f *fakeRepo) SearchRequests(_ context.Context, _ string, query string, limit int) ([]*store.Request, error) {
|
||||
if query == "" {
|
||||
return f.requests, nil
|
||||
}
|
||||
var matched []*store.Request
|
||||
for _, r := range f.requests {
|
||||
if strings.Contains(r.Method, query) ||
|
||||
strings.Contains(r.Path, query) ||
|
||||
strings.Contains(r.Headers, query) ||
|
||||
strings.Contains(r.Query, query) ||
|
||||
strings.Contains(string(r.Body), query) {
|
||||
matched = append(matched, r)
|
||||
if limit > 0 && len(matched) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
func (f *fakeRepo) GetMock(_ context.Context, endpointID string) (*store.MockConfig, error) {
|
||||
m, ok := f.mocks[endpointID]
|
||||
if !ok {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
func (f *fakeRepo) SetMock(_ context.Context, mock *store.MockConfig) error {
|
||||
f.mocks[mock.EndpointID] = mock
|
||||
return nil
|
||||
}
|
||||
func (f *fakeRepo) Close() error { return nil }
|
||||
|
||||
var errNotFound = &se{"nf"}
|
||||
|
||||
type se struct{ m string }
|
||||
|
||||
func (e *se) Error() string { return e.m }
|
||||
|
||||
// testRouter creates a chi router with all routes registered using a fake repo.
|
||||
func testRouter(repo store.Repository) chi.Router {
|
||||
@ -104,7 +25,7 @@ func testRouter(repo store.Repository) chi.Router {
|
||||
}
|
||||
|
||||
func TestHealthz(t *testing.T) {
|
||||
r := testRouter(newFakeRepo())
|
||||
r := testRouter(testutil.NewFakeRepository())
|
||||
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
@ -121,7 +42,7 @@ func TestHealthz(t *testing.T) {
|
||||
func TestCaptureRecordsAllVerbs(t *testing.T) {
|
||||
methods := []string{"GET", "POST", "PUT", "PATCH", "DELETE"}
|
||||
for _, m := range methods {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
r := testRouter(repo)
|
||||
|
||||
body := ""
|
||||
@ -138,11 +59,11 @@ func TestCaptureRecordsAllVerbs(t *testing.T) {
|
||||
if w.Code != 200 {
|
||||
t.Errorf("%s: expected 200, got %d", m, w.Code)
|
||||
}
|
||||
if len(repo.requests) != 1 {
|
||||
t.Errorf("%s: expected 1 request, got %d", m, len(repo.requests))
|
||||
if len(repo.Requests) != 1 {
|
||||
t.Errorf("%s: expected 1 request, got %d", m, len(repo.Requests))
|
||||
continue
|
||||
}
|
||||
reqCaptured := repo.requests[0]
|
||||
reqCaptured := repo.Requests[0]
|
||||
if reqCaptured.Method != m {
|
||||
t.Errorf("%s: wrong method %s", m, reqCaptured.Method)
|
||||
}
|
||||
@ -150,7 +71,7 @@ func TestCaptureRecordsAllVerbs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSSEStreamReceivesEventOnCapture(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
srv := httptest.NewServer(testRouter(repo))
|
||||
defer srv.Close()
|
||||
|
||||
@ -224,7 +145,7 @@ func TestSSEStreamReceivesEventOnCapture(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInspectReturnsHTML(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{
|
||||
Method: "POST",
|
||||
@ -290,7 +211,7 @@ func TestInspectReturnsHTML(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInspectEmptyState(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "new-ep")
|
||||
|
||||
r := testRouter(repo)
|
||||
@ -320,7 +241,7 @@ func TestInspectEmptyState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInspectAutoCreatesEndpoint(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
|
||||
// The endpoint doesn't exist yet.
|
||||
r := testRouter(repo)
|
||||
@ -350,7 +271,7 @@ func TestInspectAutoCreatesEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCaptureReturnsJSON200(t *testing.T) {
|
||||
r := testRouter(newFakeRepo())
|
||||
r := testRouter(testutil.NewFakeRepository())
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, httptest.NewRequest("GET", "/t", nil))
|
||||
|
||||
@ -369,7 +290,7 @@ func TestCaptureReturnsJSON200(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMockSetsAndConfiguresResponse(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "mock-ep")
|
||||
|
||||
r := testRouter(repo)
|
||||
@ -399,7 +320,7 @@ func TestMockSetsAndConfiguresResponse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMockReturnsConfiguredResponseOnCapture(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "mock-cap")
|
||||
// Pre-set a mock.
|
||||
repo.SetMock(nil, &store.MockConfig{
|
||||
@ -427,16 +348,16 @@ func TestMockReturnsConfiguredResponseOnCapture(t *testing.T) {
|
||||
}
|
||||
|
||||
// Request should still be captured even when mock responds.
|
||||
if len(repo.requests) != 1 {
|
||||
t.Fatalf("expected 1 captured request, got %d", len(repo.requests))
|
||||
if len(repo.Requests) != 1 {
|
||||
t.Fatalf("expected 1 captured request, got %d", len(repo.Requests))
|
||||
}
|
||||
if repo.requests[0].Method != "POST" {
|
||||
t.Errorf("captured method: %s", repo.requests[0].Method)
|
||||
if repo.Requests[0].Method != "POST" {
|
||||
t.Errorf("captured method: %s", repo.Requests[0].Method)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockAutoCreatesEndpointOnSet(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
r := testRouter(repo)
|
||||
|
||||
// Set a mock on an endpoint that doesn't exist yet.
|
||||
|
||||
@ -8,13 +8,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/elfoundation/hatch/internal/store"
|
||||
"github.com/elfoundation/hatch/internal/testutil"
|
||||
)
|
||||
|
||||
func TestReplayMissingTargetURL(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{Method: "POST", Path: "/webhook", Headers: "{}"})
|
||||
reqID := repo.requests[0].ID
|
||||
reqID := repo.Requests[0].ID
|
||||
|
||||
r := testRouter(repo)
|
||||
body := `{}`
|
||||
@ -34,10 +35,10 @@ func TestReplayMissingTargetURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplayInvalidScheme(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{Method: "GET", Path: "/", Headers: "{}"})
|
||||
reqID := repo.requests[0].ID
|
||||
reqID := repo.Requests[0].ID
|
||||
|
||||
r := testRouter(repo)
|
||||
body := `{"target_url": "ftp://bad.scheme/"}`
|
||||
@ -52,10 +53,10 @@ func TestReplayInvalidScheme(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplaySSRFBlocksLocalhost(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{Method: "GET", Path: "/", Headers: "{}"})
|
||||
reqID := repo.requests[0].ID
|
||||
reqID := repo.Requests[0].ID
|
||||
|
||||
r := testRouter(repo)
|
||||
body := `{"target_url": "http://localhost:8080/"}`
|
||||
@ -70,10 +71,10 @@ func TestReplaySSRFBlocksLocalhost(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplaySSRFBlocksPrivate(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{Method: "GET", Path: "/", Headers: "{}"})
|
||||
reqID := repo.requests[0].ID
|
||||
reqID := repo.Requests[0].ID
|
||||
|
||||
r := testRouter(repo)
|
||||
for _, addr := range []string{
|
||||
@ -96,7 +97,7 @@ func TestReplaySSRFBlocksPrivate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReplayNotFound(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
r := testRouter(repo)
|
||||
body := `{"target_url": "https://example.com/"}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/e/ep/requests/nonexistent/replay", strings.NewReader(body))
|
||||
@ -118,7 +119,7 @@ func TestReplaySuccess(t *testing.T) {
|
||||
}))
|
||||
defer sink.Close()
|
||||
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
repo.CreateEndpoint(nil, "ep")
|
||||
repo.AppendRequest(nil, "ep", &store.Request{
|
||||
Method: "POST",
|
||||
@ -127,7 +128,7 @@ func TestReplaySuccess(t *testing.T) {
|
||||
Query: "foo=bar",
|
||||
Body: []byte(`{"msg":"hello"}`),
|
||||
})
|
||||
reqID := repo.requests[0].ID
|
||||
reqID := repo.Requests[0].ID
|
||||
|
||||
// Set env to allow private replay since httptest server is on loopback.
|
||||
t.Setenv("HATCH_ALLOW_PRIVATE_REPLAY", "true")
|
||||
@ -175,7 +176,7 @@ func TestReplayE2ECaptureThenReplay(t *testing.T) {
|
||||
defer sink.Close()
|
||||
|
||||
// Step 2: Capture a request.
|
||||
repo := newFakeRepo()
|
||||
repo := testutil.NewFakeRepository()
|
||||
rt := testRouter(repo)
|
||||
|
||||
captureReq := httptest.NewRequest("POST", "/e2e-test", strings.NewReader(`{"order":"1"}`))
|
||||
@ -183,10 +184,10 @@ func TestReplayE2ECaptureThenReplay(t *testing.T) {
|
||||
captureW := httptest.NewRecorder()
|
||||
rt.ServeHTTP(captureW, captureReq)
|
||||
|
||||
if len(repo.requests) != 1 {
|
||||
t.Fatalf("expected 1 captured request, got %d", len(repo.requests))
|
||||
if len(repo.Requests) != 1 {
|
||||
t.Fatalf("expected 1 captured request, got %d", len(repo.Requests))
|
||||
}
|
||||
captured := repo.requests[0]
|
||||
captured := repo.Requests[0]
|
||||
if captured.Method != "POST" {
|
||||
t.Errorf("captured method: %s", captured.Method)
|
||||
}
|
||||
|
||||
98
internal/testutil/fake_repo.go
Normal file
98
internal/testutil/fake_repo.go
Normal file
@ -0,0 +1,98 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/elfoundation/hatch/internal/store"
|
||||
)
|
||||
|
||||
// FakeRepository implements store.Repository for tests.
|
||||
type FakeRepository struct {
|
||||
Endpoints map[string]*store.Endpoint
|
||||
Requests []*store.Request
|
||||
Mocks map[string]*store.MockConfig
|
||||
}
|
||||
|
||||
// NewFakeRepository creates a new FakeRepository.
|
||||
func NewFakeRepository() *FakeRepository {
|
||||
return &FakeRepository{
|
||||
Endpoints: map[string]*store.Endpoint{},
|
||||
Mocks: map[string]*store.MockConfig{},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FakeRepository) CreateEndpoint(_ context.Context, url string) (*store.Endpoint, error) {
|
||||
e := &store.Endpoint{ID: url, URL: url, CreatedAt: "t", UpdatedAt: "t"}
|
||||
f.Endpoints[url] = e
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) GetEndpoint(_ context.Context, id string) (*store.Endpoint, error) {
|
||||
e, ok := f.Endpoints[id]
|
||||
if !ok {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) AppendRequest(_ context.Context, eid string, r *store.Request) error {
|
||||
r.ID = "req-" + string(rune(len(f.Requests)+'0'))
|
||||
r.EndpointID = eid
|
||||
f.Requests = append(f.Requests, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) GetRequest(_ context.Context, id string) (*store.Request, error) {
|
||||
for _, r := range f.Requests {
|
||||
if r.ID == id {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return nil, errNotFound
|
||||
}
|
||||
|
||||
func (f *FakeRepository) ListRequests(_ context.Context, _ string, _ int) ([]*store.Request, error) {
|
||||
return f.Requests, nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) SearchRequests(_ context.Context, _ string, query string, limit int) ([]*store.Request, error) {
|
||||
if query == "" {
|
||||
return f.Requests, nil
|
||||
}
|
||||
var matched []*store.Request
|
||||
for _, r := range f.Requests {
|
||||
if strings.Contains(r.Method, query) ||
|
||||
strings.Contains(r.Path, query) ||
|
||||
strings.Contains(r.Headers, query) ||
|
||||
strings.Contains(r.Query, query) ||
|
||||
strings.Contains(string(r.Body), query) {
|
||||
matched = append(matched, r)
|
||||
if limit > 0 && len(matched) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) GetMock(_ context.Context, endpointID string) (*store.MockConfig, error) {
|
||||
m, ok := f.Mocks[endpointID]
|
||||
if !ok {
|
||||
return nil, errNotFound
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) SetMock(_ context.Context, mock *store.MockConfig) error {
|
||||
f.Mocks[mock.EndpointID] = mock
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRepository) Close() error { return nil }
|
||||
|
||||
type notFoundError struct{}
|
||||
|
||||
func (e *notFoundError) Error() string { return "not found" }
|
||||
|
||||
var errNotFound = ¬FoundError{}
|
||||
Loading…
Reference in New Issue
Block a user