Files
W04_048_Zagar_Database/internal/handler/fakes_test.go
T

238 lines
5.2 KiB
Go

package handler
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"reflect"
"sort"
"strings"
"sync"
"testing"
"5803025048/W04/internal/middleware"
"5803025048/W04/internal/models"
"5803025048/W04/internal/store"
)
// ---------- fake stores (in-memory, buatan tangan) ----------
type fakeUserStore struct {
mu sync.Mutex
users map[string]*models.User // key: username
createErr error
getErr error
}
func newFakeUserStore() *fakeUserStore {
return &fakeUserStore{users: map[string]*models.User{}}
}
func (f *fakeUserStore) Create(_ context.Context, user *models.User) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.createErr != nil {
return f.createErr
}
if _, exists := f.users[user.Username]; exists {
return store.ErrUsernameTaken
}
cp := *user
f.users[user.Username] = &cp
return nil
}
func (f *fakeUserStore) GetByUsername(_ context.Context, username string) (*models.User, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.getErr != nil {
return nil, f.getErr
}
u, ok := f.users[username]
if !ok {
return nil, store.ErrNotFound
}
cp := *u
return &cp, nil
}
type fakeSessionStore struct {
mu sync.Mutex
sessions map[string]*models.Session // key: token
createErr error
getErr error
deleteErr error
}
func newFakeSessionStore() *fakeSessionStore {
return &fakeSessionStore{sessions: map[string]*models.Session{}}
}
func (f *fakeSessionStore) Create(_ context.Context, session *models.Session) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.createErr != nil {
return f.createErr
}
cp := *session
f.sessions[session.Token] = &cp
return nil
}
func (f *fakeSessionStore) Get(_ context.Context, token string) (*models.Session, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.getErr != nil {
return nil, f.getErr
}
s, ok := f.sessions[token]
if !ok {
return nil, store.ErrNotFound
}
cp := *s
return &cp, nil
}
func (f *fakeSessionStore) Delete(_ context.Context, token string) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.deleteErr != nil {
return f.deleteErr
}
delete(f.sessions, token)
return nil
}
func (f *fakeSessionStore) has(token string) bool {
f.mu.Lock()
defer f.mu.Unlock()
_, ok := f.sessions[token]
return ok
}
type fakeSnippetStore struct {
mu sync.Mutex
snippets map[string]*models.Snippet // key: id
createErr error
getErr error
listErr error
deleteErr error
}
func newFakeSnippetStore() *fakeSnippetStore {
return &fakeSnippetStore{snippets: map[string]*models.Snippet{}}
}
func (f *fakeSnippetStore) seed(id, title, content, userID string) {
f.mu.Lock()
defer f.mu.Unlock()
f.snippets[id] = &models.Snippet{ID: id, Title: title, Content: content, UserID: userID}
}
func (f *fakeSnippetStore) Create(_ context.Context, snippet *models.Snippet) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.createErr != nil {
return f.createErr
}
cp := *snippet
f.snippets[snippet.ID] = &cp
return nil
}
func (f *fakeSnippetStore) GetByID(_ context.Context, id string) (*models.Snippet, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.getErr != nil {
return nil, f.getErr
}
s, ok := f.snippets[id]
if !ok {
return nil, store.ErrNotFound
}
cp := *s
return &cp, nil
}
// GetByUserID sengaja mengembalikan nil (bukan slice kosong) jika tidak ada hasil,
// supaya handler terbukti menghasilkan "[]" dan bukan "null".
func (f *fakeSnippetStore) GetByUserID(_ context.Context, userID string) ([]*models.Snippet, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.listErr != nil {
return nil, f.listErr
}
var out []*models.Snippet
for _, s := range f.snippets {
if s.UserID == userID {
cp := *s
out = append(out, &cp)
}
}
return out, nil
}
func (f *fakeSnippetStore) Delete(_ context.Context, id string) error {
f.mu.Lock()
defer f.mu.Unlock()
if f.deleteErr != nil {
return f.deleteErr
}
if _, ok := f.snippets[id]; !ok {
return store.ErrNotFound
}
delete(f.snippets, id)
return nil
}
func (f *fakeSnippetStore) has(id string) bool {
f.mu.Lock()
defer f.mu.Unlock()
_, ok := f.snippets[id]
return ok
}
// ---------- helper tes ----------
func newJSONRequest(method, target, body string) *http.Request {
req := httptest.NewRequest(method, target, strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
return req
}
func withUser(req *http.Request, userID string) *http.Request {
return req.WithContext(context.WithValue(req.Context(), middleware.UserIDKey, userID))
}
func assertJSONContentType(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "application/json") {
t.Fatalf("Content-Type = %q, want application/json", ct)
}
}
func decodeMap(t *testing.T, raw []byte) map[string]any {
t.Helper()
var m map[string]any
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatalf("body is not a JSON object: %v (body=%q)", err, string(raw))
}
return m
}
func assertKeys(t *testing.T, m map[string]any, want ...string) {
t.Helper()
got := make([]string, 0, len(m))
for k := range m {
got = append(got, k)
}
sort.Strings(got)
sort.Strings(want)
if !reflect.DeepEqual(got, want) {
t.Fatalf("keys = %v, want exactly %v", got, want)
}
}
func errNotFoundForTest() error { return store.ErrNotFound }