|
1 | 1 | import { afterEach, beforeEach, describe, expect, mock, it } from 'bun:test' |
2 | 2 | import { NextRequest } from 'next/server' |
3 | 3 |
|
| 4 | +import { AnalyticsEvent } from '@codebuff/common/constants/analytics-events' |
4 | 5 | import { TEST_USER_ID } from '@codebuff/common/constants/paths' |
5 | 6 | import { |
6 | 7 | FREEBUFF_DEEPSEEK_V4_FLASH_MODEL_ID, |
@@ -626,6 +627,72 @@ describe('/api/v1/chat/completions POST endpoint', () => { |
626 | 627 | FETCH_PATH_TEST_TIMEOUT_MS, |
627 | 628 | ) |
628 | 629 |
|
| 630 | + it( |
| 631 | + 'includes full freebuff access tier on successful usage analytics', |
| 632 | + async () => { |
| 633 | + const originalRandom = Math.random |
| 634 | + Math.random = () => 0 |
| 635 | + try { |
| 636 | + const req = new NextRequest( |
| 637 | + 'http://localhost:3000/api/v1/chat/completions', |
| 638 | + { |
| 639 | + method: 'POST', |
| 640 | + headers: allowedFreeModeHeaders('test-api-key-new-free'), |
| 641 | + body: JSON.stringify({ |
| 642 | + model: 'minimax/minimax-m2.7', |
| 643 | + stream: false, |
| 644 | + codebuff_metadata: { |
| 645 | + run_id: 'run-free', |
| 646 | + client_id: 'test-client-id-123', |
| 647 | + cost_mode: 'free', |
| 648 | + }, |
| 649 | + }), |
| 650 | + }, |
| 651 | + ) |
| 652 | + |
| 653 | + const response = await postChatCompletionsForTest({ |
| 654 | + req, |
| 655 | + getUserInfoFromApiKey: mockGetUserInfoFromApiKey, |
| 656 | + logger: mockLogger, |
| 657 | + trackEvent: mockTrackEvent, |
| 658 | + getUserUsageData: mockGetUserUsageData, |
| 659 | + getAgentRunFromId: mockGetAgentRunFromId, |
| 660 | + fetch: mockFetch, |
| 661 | + insertMessageBigquery: mockInsertMessageBigquery, |
| 662 | + loggerWithContext: mockLoggerWithContext, |
| 663 | + checkSessionAdmissible: mockCheckSessionAdmissibleAllow, |
| 664 | + }) |
| 665 | + |
| 666 | + expect(response.status).toBe(200) |
| 667 | + |
| 668 | + const trackedEvents = ( |
| 669 | + mockTrackEvent as ReturnType<typeof mock> |
| 670 | + ).mock.calls.map( |
| 671 | + ([params]) => params as Parameters<TrackEventFn>[0], |
| 672 | + ) |
| 673 | + const requestEvent = trackedEvents.find( |
| 674 | + ({ event }) => event === AnalyticsEvent.CHAT_COMPLETIONS_REQUEST, |
| 675 | + ) |
| 676 | + const generationEvent = trackedEvents.find( |
| 677 | + ({ event }) => |
| 678 | + event === AnalyticsEvent.CHAT_COMPLETIONS_GENERATION_STARTED, |
| 679 | + ) |
| 680 | + |
| 681 | + expect(requestEvent?.properties).toMatchObject({ |
| 682 | + freebuff: true, |
| 683 | + accessTier: 'full', |
| 684 | + }) |
| 685 | + expect(generationEvent?.properties).toMatchObject({ |
| 686 | + freebuff: true, |
| 687 | + accessTier: 'full', |
| 688 | + }) |
| 689 | + } finally { |
| 690 | + Math.random = originalRandom |
| 691 | + } |
| 692 | + }, |
| 693 | + FETCH_PATH_TEST_TIMEOUT_MS, |
| 694 | + ) |
| 695 | + |
629 | 696 | it( |
630 | 697 | 'lets a BYOK free-tier new account through the paid-plan gate', |
631 | 698 | async () => { |
@@ -750,6 +817,19 @@ describe('/api/v1/chat/completions POST endpoint', () => { |
750 | 817 | const body = await response.json() |
751 | 818 | expect(body.error).toBe('session_model_mismatch') |
752 | 819 | expect(checkSessionAdmissible).toHaveBeenCalledTimes(0) |
| 820 | + const validationEvent = ( |
| 821 | + mockTrackEvent as ReturnType<typeof mock> |
| 822 | + ).mock.calls |
| 823 | + .map(([params]) => params as Parameters<TrackEventFn>[0]) |
| 824 | + .find( |
| 825 | + ({ event, properties }) => |
| 826 | + event === AnalyticsEvent.CHAT_COMPLETIONS_VALIDATION_ERROR && |
| 827 | + properties?.error === 'session_model_mismatch', |
| 828 | + ) |
| 829 | + expect(validationEvent?.properties).toMatchObject({ |
| 830 | + freebuff: true, |
| 831 | + accessTier: 'limited', |
| 832 | + }) |
753 | 833 | }) |
754 | 834 |
|
755 | 835 | it('classifies anonymized Cloudflare country codes as limited access', async () => { |
|
0 commit comments