2026-05-18 17:55:07 +03:00
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
import { mount } from '@vue/test-utils';
|
|
|
|
|
import { createVuetify } from 'vuetify';
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import AdminSupplierIntegrationView from '../../resources/js/views/admin/AdminSupplierIntegrationView.vue';
|
|
|
|
|
|
|
|
|
|
vi.mock('axios');
|
|
|
|
|
|
2026-05-23 15:35:04 +03:00
|
|
|
const vuetify = createVuetify();
|
2026-05-18 17:55:07 +03:00
|
|
|
|
|
|
|
|
const healthPayload = {
|
|
|
|
|
health: { last_run_at: '2026-05-18T12:00:00Z', last_status: 'ok', drift_ratio: 0.02, webhook_state: 'live' },
|
|
|
|
|
history: [
|
|
|
|
|
{
|
2026-06-17 05:17:12 +03:00
|
|
|
started_at: '2026-05-18T12:00:00Z',
|
|
|
|
|
finished_at: '2026-05-18T12:01:00Z',
|
|
|
|
|
window_start: '2026-05-17T00:00:00Z',
|
|
|
|
|
window_end: '2026-05-18T12:00:00Z',
|
|
|
|
|
status: 'ok',
|
|
|
|
|
total_csv_rows: 100,
|
|
|
|
|
matched_count: 98,
|
|
|
|
|
recovered_count: 2,
|
|
|
|
|
drift_ratio: 0.02,
|
2026-05-18 17:55:07 +03:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function mountView() {
|
|
|
|
|
return mount(AdminSupplierIntegrationView, { global: { plugins: [vuetify] } });
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-25 18:52:39 +03:00
|
|
|
const syncRunsPayload = {
|
|
|
|
|
runs: [
|
|
|
|
|
{
|
|
|
|
|
started_at: '2026-06-25T15:05:00Z',
|
|
|
|
|
finished_at: '2026-06-25T15:47:00Z',
|
|
|
|
|
groups_total: 180,
|
|
|
|
|
synced_ok: 312,
|
|
|
|
|
manual_queued: 1,
|
|
|
|
|
deferred: 0,
|
|
|
|
|
failed: 0,
|
|
|
|
|
status: 'partial',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-18 17:55:07 +03:00
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
2026-06-25 18:52:39 +03:00
|
|
|
(axios.get as ReturnType<typeof vi.fn>).mockImplementation((url: string) => {
|
|
|
|
|
if (url === '/api/admin/supplier-integration/sync-runs') {
|
|
|
|
|
return Promise.resolve({ data: syncRunsPayload });
|
|
|
|
|
}
|
|
|
|
|
if (url === '/api/admin/supplier-integration/manual-queue') {
|
|
|
|
|
return Promise.resolve({ data: { queue: [] } });
|
|
|
|
|
}
|
|
|
|
|
if (url === '/api/admin/supplier-integration/export-mode') {
|
|
|
|
|
return Promise.resolve({ data: { mode: 'batch' } });
|
|
|
|
|
}
|
|
|
|
|
return Promise.resolve({ data: healthPayload });
|
|
|
|
|
});
|
2026-05-18 17:55:07 +03:00
|
|
|
(axios.post as ReturnType<typeof vi.fn>).mockResolvedValue({ data: { dispatched: true } });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('AdminSupplierIntegrationView', () => {
|
|
|
|
|
it('loads channel health on mount', async () => {
|
|
|
|
|
const wrapper = mountView();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
|
expect(axios.get).toHaveBeenCalledWith('/api/admin/supplier-integration');
|
|
|
|
|
expect(wrapper.text()).toContain('live');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('renders reconcile history rows', async () => {
|
|
|
|
|
const wrapper = mountView();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
expect(wrapper.text()).toContain('100');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('triggers manual reconcile on button click', async () => {
|
|
|
|
|
const wrapper = mountView();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
|
await wrapper.find('[data-test="reconcile-now"]').trigger('click');
|
|
|
|
|
expect(axios.post).toHaveBeenCalledWith('/api/admin/supplier-integration/reconcile');
|
|
|
|
|
});
|
2026-06-25 18:52:39 +03:00
|
|
|
|
|
|
|
|
// --- Эпик 5: история вечерних заливок ---
|
|
|
|
|
|
|
|
|
|
it('loads evening-upload history on mount', async () => {
|
|
|
|
|
mountView();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
|
expect(axios.get).toHaveBeenCalledWith('/api/admin/supplier-integration/sync-runs');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('renders sync-runs table with totals + human status', async () => {
|
|
|
|
|
const wrapper = mountView();
|
|
|
|
|
await new Promise((r) => setTimeout(r, 0));
|
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
expect(wrapper.find('[data-testid="sync-runs-table"]').exists()).toBe(true);
|
|
|
|
|
const rows = wrapper.findAll('[data-testid="sync-run-row"]');
|
|
|
|
|
expect(rows.length).toBe(1);
|
|
|
|
|
expect(wrapper.text()).toContain('180'); // групп
|
|
|
|
|
expect(wrapper.text()).toContain('312'); // готово
|
|
|
|
|
expect(wrapper.text()).toContain('Частично'); // human status (partial)
|
|
|
|
|
});
|
2026-05-18 17:55:07 +03:00
|
|
|
});
|