add mobile api unit tests for gyms and recommendations

This commit is contained in:
echo 2026-03-29 15:34:43 +02:00
parent 80110acbf7
commit aa662a9b74
3 changed files with 140 additions and 9 deletions

View File

@ -1,11 +1,19 @@
import 'react-native-gesture-handler/jestSetup'
try {
require("react-native-gesture-handler/jestSetup");
} catch {
// Package may be absent in minimal test environments
}
jest.mock('react-native-reanimated', () => {
const Reanimated = require('react-native-reanimated/mock')
Reanimated.default.call = () => {}
return Reanimated
})
jest.mock(
"react-native-reanimated",
() => {
const Reanimated = require("react-native-reanimated/mock");
Reanimated.default.call = () => {};
return Reanimated;
},
{ virtual: true },
);
jest.mock('@expo/vector-icons', () => ({
Ionicons: 'Ionicons',
}))
jest.mock("@expo/vector-icons", () => ({
Ionicons: "Ionicons",
}));

View File

@ -0,0 +1,55 @@
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { gymsApi } from "../gyms";
import { apiClient, withAuth } from "../client";
jest.mock("../client", () => ({
apiClient: {
get: jest.fn(),
patch: jest.fn(),
},
withAuth: jest.fn((token?: string | null) =>
token ? { headers: { Authorization: `Bearer ${token}` } } : {},
),
}));
describe("gymsApi", () => {
const getMock = apiClient.get as any;
const patchMock = apiClient.patch as any;
beforeEach(() => {
jest.clearAllMocks();
});
it("returns array payload from getGyms", async () => {
getMock.mockResolvedValue({
data: [{ id: "gym_1", name: "Gym One" }],
});
const result = await gymsApi.getGyms("token_1");
expect(result).toEqual([{ id: "gym_1", name: "Gym One" }]);
expect(withAuth).toHaveBeenCalledWith("token_1");
});
it("returns nested data payload from getGyms", async () => {
getMock.mockResolvedValue({
data: { data: [{ id: "gym_2", name: "Gym Two" }] },
});
const result = await gymsApi.getGyms(null);
expect(result).toEqual([{ id: "gym_2", name: "Gym Two" }]);
});
it("patches selected gym for current user", async () => {
patchMock.mockResolvedValue({});
await gymsApi.updateUserGym("gym_2", "token_2");
expect(apiClient.patch).toHaveBeenCalledWith(
"/api/users/gym",
{ gymId: "gym_2" },
expect.any(Object),
);
});
});

View File

@ -0,0 +1,68 @@
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import {
approveRecommendation,
generateRecommendation,
getRecommendations,
} from "../recommendations";
import { apiClient, withAuth } from "../client";
jest.mock("../client", () => ({
apiClient: {
get: jest.fn(),
post: jest.fn(),
},
withAuth: jest.fn((token?: string | null) =>
token ? { headers: { Authorization: `Bearer ${token}` } } : {},
),
}));
describe("recommendations api", () => {
const getMock = apiClient.get as any;
const postMock = apiClient.post as any;
beforeEach(() => {
jest.clearAllMocks();
});
it("returns normalized list from standardized response", async () => {
getMock.mockResolvedValue({
data: {
success: true,
data: [{ id: "rec_1", status: "pending" }],
},
});
const result = await getRecommendations("user_1", "token_1");
expect(result).toEqual([{ id: "rec_1", status: "pending" }]);
expect(withAuth).toHaveBeenCalledWith("token_1");
});
it("falls back to legacy response for generate", async () => {
postMock.mockResolvedValue({
data: { id: "rec_2", status: "pending" },
});
const result = await generateRecommendation({ userId: "user_1" }, null);
expect(result).toEqual({ id: "rec_2", status: "pending" });
});
it("sends approval payload without approvedBy", async () => {
postMock.mockResolvedValue({
data: {
success: true,
data: { id: "rec_3", status: "approved" },
},
});
const result = await approveRecommendation("rec_3", "token_3");
expect(result).toEqual({ id: "rec_3", status: "approved" });
expect(apiClient.post).toHaveBeenCalledWith(
"/api/recommendations/approve",
{ recommendationId: "rec_3", status: "approved" },
expect.any(Object),
);
});
});