Add centralized API client with axios interceptors, remove vue-axios

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 12:44:33 +08:00
parent 6af7253d08
commit 147b16ca34
29 changed files with 301 additions and 270 deletions

View File

@@ -5,6 +5,13 @@ vi.mock('@/module/apiError.js', () => ({
default: vi.fn(),
}));
const { mockGet, mockPost, mockPut, mockDelete } = vi.hoisted(() => ({
mockGet: vi.fn(), mockPost: vi.fn(), mockPut: vi.fn(), mockDelete: vi.fn(),
}));
vi.mock('@/api/client.js', () => ({
default: { get: mockGet, post: mockPost, put: mockPut, delete: mockDelete },
}));
// Mock login store to avoid its side effects
vi.mock('@/stores/login.ts', () => {
const { defineStore } = require('pinia');
@@ -24,17 +31,10 @@ import useAcctMgmtStore from '@/stores/acctMgmt.ts';
describe('acctMgmtStore', () => {
let store;
const mockAxios = {
get: vi.fn(),
post: vi.fn(),
put: vi.fn(),
delete: vi.fn(),
};
beforeEach(() => {
setActivePinia(createPinia());
store = useAcctMgmtStore();
store.$axios = mockAxios;
vi.clearAllMocks();
});
@@ -84,12 +84,12 @@ describe('acctMgmtStore', () => {
describe('createNewAccount', () => {
it('posts to /api/users and sets flag on success', async () => {
mockAxios.post.mockResolvedValue({ status: 200 });
mockPost.mockResolvedValue({ status: 200 });
const user = { username: 'newuser', password: 'pass' };
await store.createNewAccount(user);
expect(mockAxios.post).toHaveBeenCalledWith(
expect(mockPost).toHaveBeenCalledWith(
'/api/users', user,
);
expect(store.isOneAccountJustCreate).toBe(true);
@@ -99,18 +99,18 @@ describe('acctMgmtStore', () => {
describe('deleteAccount', () => {
it('returns true on success', async () => {
mockAxios.delete.mockResolvedValue({ status: 200 });
mockDelete.mockResolvedValue({ status: 200 });
const result = await store.deleteAccount('alice');
expect(mockAxios.delete).toHaveBeenCalledWith(
expect(mockDelete).toHaveBeenCalledWith(
'/api/users/alice',
);
expect(result).toBe(true);
});
it('returns false on error', async () => {
mockAxios.delete.mockRejectedValue(new Error('fail'));
mockDelete.mockRejectedValue(new Error('fail'));
const result = await store.deleteAccount('alice');
@@ -120,7 +120,7 @@ describe('acctMgmtStore', () => {
describe('editAccount', () => {
it('puts edited data', async () => {
mockAxios.put.mockResolvedValue({ status: 200 });
mockPut.mockResolvedValue({ status: 200 });
const detail = {
username: 'alice',
password: 'newpw',
@@ -130,7 +130,7 @@ describe('acctMgmtStore', () => {
const result = await store.editAccount('alice', detail);
expect(mockAxios.put).toHaveBeenCalledWith(
expect(mockPut).toHaveBeenCalledWith(
'/api/users/alice',
expect.objectContaining({ password: 'newpw' }),
);
@@ -140,11 +140,11 @@ describe('acctMgmtStore', () => {
describe('addRoleToUser', () => {
it('puts role assignment', async () => {
mockAxios.put.mockResolvedValue({ status: 200 });
mockPut.mockResolvedValue({ status: 200 });
const result = await store.addRoleToUser('alice', 'admin');
expect(mockAxios.put).toHaveBeenCalledWith(
expect(mockPut).toHaveBeenCalledWith(
'/api/users/alice/roles/admin',
);
expect(result).toBe(true);
@@ -153,11 +153,11 @@ describe('acctMgmtStore', () => {
describe('deleteRoleToUser', () => {
it('deletes role', async () => {
mockAxios.delete.mockResolvedValue({ status: 200 });
mockDelete.mockResolvedValue({ status: 200 });
const result = await store.deleteRoleToUser('alice', 'admin');
expect(mockAxios.delete).toHaveBeenCalledWith(
expect(mockDelete).toHaveBeenCalledWith(
'/api/users/alice/roles/admin',
);
expect(result).toBe(true);
@@ -166,7 +166,7 @@ describe('acctMgmtStore', () => {
describe('getUserDetail', () => {
it('fetches user and sets admin flag', async () => {
mockAxios.get.mockResolvedValue({
mockGet.mockResolvedValue({
status: 200,
data: {
username: 'alice',
@@ -181,7 +181,7 @@ describe('acctMgmtStore', () => {
});
it('returns false on error', async () => {
mockAxios.get.mockRejectedValue(new Error('not found'));
mockGet.mockRejectedValue(new Error('not found'));
const result = await store.getUserDetail('ghost');