import type { GetFeaturesResponse } from '@dify/contracts/api/console/features/types.gen' import { getPlanVectorSpaceLimitMB, parseCurrentPlan, parseVectorSpaceToMB } from '../index' describe('billing utils', () => { // parseVectorSpaceToMB tests describe('parseVectorSpaceToMB', () => { it('should parse MB values correctly', () => { expect(parseVectorSpaceToMB('50MB')).toBe(50) expect(parseVectorSpaceToMB('100MB')).toBe(100) }) it('should parse GB values and convert to MB', () => { expect(parseVectorSpaceToMB('5GB')).toBe(5 * 1024) expect(parseVectorSpaceToMB('20GB')).toBe(20 * 1024) }) it('should be case insensitive', () => { expect(parseVectorSpaceToMB('50mb')).toBe(50) expect(parseVectorSpaceToMB('5gb')).toBe(5 * 1024) }) it('should return 0 for invalid format', () => { expect(parseVectorSpaceToMB('50')).toBe(0) expect(parseVectorSpaceToMB('invalid')).toBe(0) expect(parseVectorSpaceToMB('')).toBe(0) expect(parseVectorSpaceToMB('50TB')).toBe(0) }) }) // getPlanVectorSpaceLimitMB tests describe('getPlanVectorSpaceLimitMB', () => { it('should return correct vector space for sandbox plan', () => { expect(getPlanVectorSpaceLimitMB('sandbox')).toBe(50) }) it('should return correct vector space for professional plan', () => { expect(getPlanVectorSpaceLimitMB('professional')).toBe(5 * 1024) }) it('should return correct vector space for team plan', () => { expect(getPlanVectorSpaceLimitMB('team')).toBe(20 * 1024) }) }) // parseCurrentPlan tests describe('parseCurrentPlan', () => { const createMockPlanData = ( overrides: Partial = {}, ): GetFeaturesResponse => ({ annotation_quota_limit: { size: 5, limit: 10, }, api_rate_limit: { usage: 0, limit: 5000, reset_date: -1, }, apps: { size: 2, limit: 5, }, billing: { enabled: true, subscription: { interval: '', plan: 'sandbox', }, }, can_replace_logo: false, dataset_operator_enabled: false, docs_processing: '', documents_upload_quota: { size: 20, limit: 0, }, education: { activated: false, enabled: false, }, enable_skill: false, human_input_email_delivery_enabled: false, is_allow_transfer_workspace: false, knowledge_pipeline: { publish_enabled: false, }, knowledge_rate_limit: 0, members: { size: 1, limit: 1, }, model_load_balancing_enabled: false, next_credit_reset_date: 0, trigger_event: { usage: 0, limit: 3000, reset_date: -1, }, vector_space: null, webapp_copyright_enabled: false, workspace_members: { enabled: false, size: 0, limit: 0, }, ...overrides, }) it('should parse plan type correctly', () => { const data = createMockPlanData() const result = parseCurrentPlan(data) expect(result.type).toBe('sandbox') }) it('should parse usage values correctly', () => { const data = createMockPlanData() const result = parseCurrentPlan(data) expect(result.usage.vectorSpace).toBe(0) expect(result.usage.buildApps).toBe(2) expect(result.usage.teamMembers).toBe(1) expect(result.usage.annotatedResponse).toBe(5) expect(result.usage.documentsUploadQuota).toBe(20) }) it('should parse total limits correctly', () => { const data = createMockPlanData() const result = parseCurrentPlan(data) expect(result.total.vectorSpace).toBe(50) expect(result.total.buildApps).toBe(5) expect(result.total.teamMembers).toBe(1) expect(result.total.annotatedResponse).toBe(10) }) it('should not read vector space usage from current plan info', () => { const data = createMockPlanData() const result = parseCurrentPlan(data) expect(result.usage.vectorSpace).toBe(0) expect(result.total.vectorSpace).toBe(50) }) it('should derive vector space total from plan config', () => { const data = createMockPlanData({ billing: { enabled: true, subscription: { interval: '', plan: 'professional', }, }, }) const result = parseCurrentPlan(data) expect(result.usage.vectorSpace).toBe(0) expect(result.total.vectorSpace).toBe(5 * 1024) }) it('should convert 0 limits to NUM_INFINITE (-1)', () => { const data = createMockPlanData({ documents_upload_quota: { size: 20, limit: 0, }, }) const result = parseCurrentPlan(data) expect(result.total.documentsUploadQuota).toBe(-1) }) it('should handle api_rate_limit quota', () => { const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: 0, }, }) const result = parseCurrentPlan(data) expect(result.usage.apiRateLimit).toBe(100) expect(result.total.apiRateLimit).toBe(5000) }) it('should handle trigger_event quota', () => { const data = createMockPlanData({ trigger_event: { usage: 50, limit: 3000, reset_date: 0, }, }) const result = parseCurrentPlan(data) expect(result.usage.triggerEvents).toBe(50) expect(result.total.triggerEvents).toBe(3000) }) it('should convert 0 or -1 rate limits to NUM_INFINITE', () => { const data = createMockPlanData({ api_rate_limit: { usage: 0, limit: 0, reset_date: 0, }, }) const result = parseCurrentPlan(data) expect(result.total.apiRateLimit).toBe(-1) const data2 = createMockPlanData({ api_rate_limit: { usage: 0, limit: -1, reset_date: 0, }, }) const result2 = parseCurrentPlan(data2) expect(result2.total.apiRateLimit).toBe(-1) }) it('should handle reset dates with milliseconds timestamp', () => { const futureDate = Date.now() + 86400000 // Tomorrow in ms const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: futureDate, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBe(1) }) it('should handle reset dates with seconds timestamp', () => { const futureDate = Math.floor(Date.now() / 1000) + 86400 // Tomorrow in seconds const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: futureDate, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBe(1) }) it('should handle reset dates in YYYYMMDD format', () => { const tomorrow = new Date() tomorrow.setDate(tomorrow.getDate() + 1) const year = tomorrow.getFullYear() const month = String(tomorrow.getMonth() + 1).padStart(2, '0') const day = String(tomorrow.getDate()).padStart(2, '0') const dateNumber = Number.parseInt(`${year}${month}${day}`, 10) const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: dateNumber, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBe(1) }) it('should return null for invalid reset dates', () => { const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: 0, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBeNull() }) it('should return null for negative reset dates', () => { const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: -1, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBeNull() }) it('should return null when reset date is in the past', () => { const pastDate = Date.now() - 86400000 // Yesterday const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: pastDate, }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBeNull() }) it('should return null for unrecognized date format', () => { const data = createMockPlanData({ api_rate_limit: { usage: 100, limit: 5000, reset_date: 12345, // Unrecognized format }, }) const result = parseCurrentPlan(data) expect(result.reset.apiRateLimit).toBeNull() }) }) })