package handlers import ( "encoding/json" "errors" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Tests for the read-only handlers (`internalListRuntimes`, // `internalGetRuntime`). These bypass the service layer and read // directly from `ports.RuntimeRecordStore` — see // `rtmanager/docs/services.md` §18. func TestListHandlerReturnsEmptyItemsForEmptyStore(t *testing.T) { t.Parallel() deps := Dependencies{RuntimeRecords: newFakeRuntimeRecords()} rec := drive(t, deps, http.MethodGet, "/api/v1/internal/runtimes", nil, nil) require.Equal(t, http.StatusOK, rec.Code) require.Equal(t, JSONContentType, rec.Header().Get("Content-Type")) var resp runtimesListResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) require.NotNil(t, resp.Items, "items must never be nil") assert.Empty(t, resp.Items) } func TestListHandlerReturnsEveryStoredRecord(t *testing.T) { t.Parallel() store := newFakeRuntimeRecords() store.put(sampleRunningRecord(t)) store.put(sampleStoppedRecord(t)) rec := drive(t, Dependencies{RuntimeRecords: store}, http.MethodGet, "/api/v1/internal/runtimes", nil, nil) require.Equal(t, http.StatusOK, rec.Code) var resp runtimesListResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) require.Len(t, resp.Items, 2) gotIDs := map[string]string{} for _, item := range resp.Items { gotIDs[item.GameID] = item.Status } assert.Equal(t, "running", gotIDs["game-test"]) assert.Equal(t, "stopped", gotIDs["game-stopped"]) } func TestListHandlerReturnsInternalErrorWhenStoreFails(t *testing.T) { t.Parallel() store := newFakeRuntimeRecords() store.listErr = errors.New("postgres exploded") rec := drive(t, Dependencies{RuntimeRecords: store}, http.MethodGet, "/api/v1/internal/runtimes", nil, nil) body := decodeErrorBody(t, rec, http.StatusInternalServerError) assert.Equal(t, "internal_error", body.Code) } func TestListHandlerReturnsInternalErrorWhenStoreNotWired(t *testing.T) { t.Parallel() rec := drive(t, Dependencies{}, http.MethodGet, "/api/v1/internal/runtimes", nil, nil) body := decodeErrorBody(t, rec, http.StatusInternalServerError) assert.Equal(t, "internal_error", body.Code) } func TestGetHandlerReturnsTheRecord(t *testing.T) { t.Parallel() store := newFakeRuntimeRecords() record := sampleRunningRecord(t) store.put(record) rec := drive(t, Dependencies{RuntimeRecords: store}, http.MethodGet, "/api/v1/internal/runtimes/game-test", nil, nil) resp := decodeRecordResponse(t, rec) assert.Equal(t, "game-test", resp.GameID) assert.Equal(t, "running", resp.Status) if assert.NotNil(t, resp.CurrentImageRef) { assert.Equal(t, "galaxy/game:v1.2.3", *resp.CurrentImageRef) } } func TestGetHandlerReturnsNotFoundForMissingRecord(t *testing.T) { t.Parallel() rec := drive(t, Dependencies{RuntimeRecords: newFakeRuntimeRecords()}, http.MethodGet, "/api/v1/internal/runtimes/game-missing", nil, nil) body := decodeErrorBody(t, rec, http.StatusNotFound) assert.Equal(t, "not_found", body.Code) } func TestGetHandlerReturnsInternalErrorWhenStoreFails(t *testing.T) { t.Parallel() store := newFakeRuntimeRecords() store.getErr = errors.New("transport blew up") rec := drive(t, Dependencies{RuntimeRecords: store}, http.MethodGet, "/api/v1/internal/runtimes/game-test", nil, nil) body := decodeErrorBody(t, rec, http.StatusInternalServerError) assert.Equal(t, "internal_error", body.Code) } func TestGetHandlerReturnsInternalErrorWhenStoreNotWired(t *testing.T) { t.Parallel() rec := drive(t, Dependencies{}, http.MethodGet, "/api/v1/internal/runtimes/game-test", nil, nil) body := decodeErrorBody(t, rec, http.StatusInternalServerError) assert.Equal(t, "internal_error", body.Code) }