File size: 4,859 Bytes
f0634fb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | import { afterEach, describe, expect, it, vi } from 'vitest';
import {
fetchCompleteFeedbackUpload,
fetchCreateFeedbackUploadUrl,
kimiCodeFeedbackUploadCompleteUrl,
kimiCodeFeedbackUploadUrl,
type CreateFeedbackUploadUrlBody,
} from '../src/managed-feedback-upload';
afterEach(() => {
vi.unstubAllGlobals();
vi.unstubAllEnvs();
});
const SAMPLE_BODY: CreateFeedbackUploadUrlBody = {
file_hash: 'e4d649659ca70729a510ef58f4cd062890020a1038eead5f411451fce62df415',
file_name: 'repo.zip',
file_size: 123,
feedback_id: 3,
};
describe('kimiCodeFeedbackUploadUrl', () => {
it('uses the feedback upload_url path', () => {
expect(kimiCodeFeedbackUploadUrl()).toBe('https://api.kimi.com/coding/v1/feedback/upload_url');
});
});
describe('kimiCodeFeedbackUploadCompleteUrl', () => {
it('uses the feedback upload_complete path', () => {
expect(kimiCodeFeedbackUploadCompleteUrl()).toBe(
'https://api.kimi.com/coding/v1/feedback/upload_complete',
);
});
});
describe('fetchCreateFeedbackUploadUrl', () => {
it('POSTs JSON body with bearer auth and parses upload parts', async () => {
const fetchMock = vi.fn(async () =>
new Response(
JSON.stringify({
code: 0,
upload: {
id: 28,
upload_id: 'tos-multipart-id',
part_size: 8,
total_parts: 1,
parts: [
{ part_number: 1, url: 'https://example.test/part1', method: 'PUT', size: 123 },
],
},
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
),
);
vi.stubGlobal('fetch', fetchMock);
const result = await fetchCreateFeedbackUploadUrl('access-token', SAMPLE_BODY);
expect(result).toEqual({
kind: 'ok',
upload_id: 28,
parts: [{ part_number: 1, url: 'https://example.test/part1', method: 'PUT', size: 123 }],
});
const calls = fetchMock.mock.calls as unknown as [string, RequestInit?][];
const [calledUrl, init] = calls[0]!;
expect(calledUrl).toBe('https://api.kimi.com/coding/v1/feedback/upload_url');
expect(init?.method).toBe('POST');
const headers = new Headers((init?.headers ?? {}) as Record<string, string>);
expect(headers.get('authorization')).toBe('Bearer access-token');
expect(JSON.parse(init?.body as string)).toEqual(SAMPLE_BODY);
});
it('returns an error when the response omits parts', async () => {
vi.stubGlobal(
'fetch',
vi.fn(async () =>
new Response(JSON.stringify({ code: 0, upload: { id: 28 } }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}),
),
);
const result = await fetchCreateFeedbackUploadUrl('access-token', SAMPLE_BODY);
expect(result).toEqual({
kind: 'error',
message: 'Feedback upload request failed: missing upload id or parts.',
});
});
it('returns an error when a part is missing required fields', async () => {
vi.stubGlobal(
'fetch',
vi.fn(async () =>
new Response(
JSON.stringify({
code: 0,
upload: { id: 28, parts: [{ part_number: 1 }] },
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
),
),
);
const result = await fetchCreateFeedbackUploadUrl('access-token', SAMPLE_BODY);
expect(result).toEqual({
kind: 'error',
message: 'Feedback upload request failed: missing upload id or parts.',
});
});
it('returns an error with status when the server responds 401', async () => {
vi.stubGlobal('fetch', vi.fn(async () => new Response('', { status: 401 })));
const result = await fetchCreateFeedbackUploadUrl('access-token', SAMPLE_BODY);
expect(result.kind).toBe('error');
if (result.kind !== 'error') return;
expect(result.status).toBe(401);
expect(result.message).toMatch(/401/);
});
});
describe('fetchCompleteFeedbackUpload', () => {
it('POSTs upload_id and parts with bearer auth', async () => {
const fetchMock = vi.fn(async () => new Response('{}', { status: 200 }));
vi.stubGlobal('fetch', fetchMock);
const result = await fetchCompleteFeedbackUpload('access-token', {
upload_id: 28,
parts: [
{ part_number: 1, etag: '"etag-1"' },
{ part_number: 2, etag: '"etag-2"' },
],
});
expect(result).toEqual({ kind: 'ok' });
const calls = fetchMock.mock.calls as unknown as [string, RequestInit?][];
const [calledUrl, init] = calls[0]!;
expect(calledUrl).toBe('https://api.kimi.com/coding/v1/feedback/upload_complete');
expect(JSON.parse(init?.body as string)).toEqual({
upload_id: 28,
parts: [
{ part_number: 1, etag: '"etag-1"' },
{ part_number: 2, etag: '"etag-2"' },
],
});
});
});
|