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 // Space-related fields Spaces map[string]*store.Space Photos map[string]*store.Photo FaceEmbeddings map[string]*store.FaceEmbedding RateLimits map[string]*store.RateLimit } // NewFakeRepository creates a new FakeRepository. func NewFakeRepository() *FakeRepository { return &FakeRepository{ Endpoints: map[string]*store.Endpoint{}, Mocks: map[string]*store.MockConfig{}, Spaces: map[string]*store.Space{}, Photos: map[string]*store.Photo{}, FaceEmbeddings: map[string]*store.FaceEmbedding{}, RateLimits: map[string]*store.RateLimit{}, } } 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 } func (f *FakeRepository) Ping(_ context.Context) error { return nil } // Space operations func (f *FakeRepository) CreateSpace(_ context.Context, space *store.Space) (*store.Space, error) { f.Spaces[space.Slug] = space return space, nil } func (f *FakeRepository) GetSpaceBySlug(_ context.Context, slug string) (*store.Space, error) { space, ok := f.Spaces[slug] if !ok { return nil, errNotFound } return space, nil } func (f *FakeRepository) UpdateSpace(_ context.Context, space *store.Space) (*store.Space, error) { f.Spaces[space.Slug] = space return space, nil } func (f *FakeRepository) DeleteSpace(_ context.Context, id string) error { for slug, space := range f.Spaces { if space.ID == id { delete(f.Spaces, slug) return nil } } return errNotFound } func (f *FakeRepository) ListPublicSpaces(_ context.Context) ([]*store.Space, error) { var spaces []*store.Space for _, space := range f.Spaces { if space.IsPublic { spaces = append(spaces, space) } } return spaces, nil } // Photo operations func (f *FakeRepository) CreatePhoto(_ context.Context, photo *store.Photo) (*store.Photo, error) { f.Photos[photo.ID] = photo return photo, nil } func (f *FakeRepository) GetPhoto(_ context.Context, id string) (*store.Photo, error) { photo, ok := f.Photos[id] if !ok { return nil, errNotFound } return photo, nil } func (f *FakeRepository) ListPhotosBySpace(_ context.Context, spaceID string, limit int) ([]*store.Photo, error) { var photos []*store.Photo for _, photo := range f.Photos { if photo.SpaceID == spaceID { photos = append(photos, photo) if limit > 0 && len(photos) >= limit { break } } } return photos, nil } func (f *FakeRepository) DeletePhoto(_ context.Context, id string) error { for photoID := range f.Photos { if photoID == id { delete(f.Photos, photoID) return nil } } return errNotFound } // Face embedding operations func (f *FakeRepository) CreateFaceEmbedding(_ context.Context, embedding *store.FaceEmbedding) (*store.FaceEmbedding, error) { f.FaceEmbeddings[embedding.ID] = embedding return embedding, nil } func (f *FakeRepository) ListFaceEmbeddingsBySpace(_ context.Context, spaceID string) ([]*store.FaceEmbedding, error) { var embeddings []*store.FaceEmbedding for _, embedding := range f.FaceEmbeddings { if embedding.SpaceID == spaceID { embeddings = append(embeddings, embedding) } } return embeddings, nil } func (f *FakeRepository) SearchFaceEmbeddings(_ context.Context, spaceID string, embedding []float32, limit int) ([]*store.FaceEmbedding, error) { // For fake repo, just return all embeddings in the space return f.ListFaceEmbeddingsBySpace(nil, spaceID) } // Rate limiting operations func (f *FakeRepository) CheckRateLimit(_ context.Context, ipAddress, spaceID string, limit, windowMinutes int) (bool, error) { // For fake repo, always allow return true, nil } func (f *FakeRepository) IncrementRateLimit(_ context.Context, ipAddress, spaceID string, windowMinutes int) error { // For fake repo, do nothing return nil } type notFoundError struct{} func (e *notFoundError) Error() string { return "not found" } var errNotFound = ¬FoundError{}