DataApi.utils.test.ts167 lines · main
| 1 | import { describe, expect, it } from 'vitest' |
| 2 | |
| 3 | import { buildEntityMaps, getApiEndpoint } from './DataApi.utils' |
| 4 | import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query' |
| 5 | import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query' |
| 6 | import type { Database } from '@/data/read-replicas/replicas-query' |
| 7 | |
| 8 | const makeDatabase = ( |
| 9 | identifier: string, |
| 10 | restUrl: string |
| 11 | ): Pick<Database, 'identifier' | 'restUrl'> => ({ identifier, restUrl }) |
| 12 | |
| 13 | const makeLoadBalancer = (endpoint: string): Pick<LoadBalancer, 'endpoint'> => ({ endpoint }) |
| 14 | |
| 15 | describe('getApiEndpoint', () => { |
| 16 | it('returns custom domain URL with /rest/v1/ when custom domain is active and primary database is selected', () => { |
| 17 | expect( |
| 18 | getApiEndpoint({ |
| 19 | selectedDatabaseId: 'project-ref', |
| 20 | projectRef: 'project-ref', |
| 21 | resolvedEndpoint: 'https://api.example.com', |
| 22 | loadBalancers: undefined, |
| 23 | selectedDatabase: makeDatabase( |
| 24 | 'project-ref', |
| 25 | 'https://project-ref.supabase.co/rest/v1/' |
| 26 | ) as Database, |
| 27 | }) |
| 28 | ).toBe('https://api.example.com/rest/v1/') |
| 29 | }) |
| 30 | |
| 31 | it('returns database restUrl when custom domain is active but a replica is selected', () => { |
| 32 | expect( |
| 33 | getApiEndpoint({ |
| 34 | selectedDatabaseId: 'replica-1', |
| 35 | projectRef: 'project-ref', |
| 36 | resolvedEndpoint: 'https://api.example.com', |
| 37 | loadBalancers: undefined, |
| 38 | selectedDatabase: makeDatabase( |
| 39 | 'replica-1', |
| 40 | 'https://replica-1.supabase.co/rest/v1/' |
| 41 | ) as Database, |
| 42 | }) |
| 43 | ).toBe('https://replica-1.supabase.co/rest/v1/') |
| 44 | }) |
| 45 | |
| 46 | it('normalizes a replica restUrl without a trailing slash', () => { |
| 47 | expect( |
| 48 | getApiEndpoint({ |
| 49 | selectedDatabaseId: 'replica-1', |
| 50 | projectRef: 'project-ref', |
| 51 | resolvedEndpoint: undefined, |
| 52 | loadBalancers: undefined, |
| 53 | selectedDatabase: makeDatabase( |
| 54 | 'replica-1', |
| 55 | 'https://replica-1.supabase.co/rest/v1' |
| 56 | ) as Database, |
| 57 | }) |
| 58 | ).toBe('https://replica-1.supabase.co/rest/v1/') |
| 59 | }) |
| 60 | |
| 61 | it('returns load balancer endpoint with /rest/v1/ when load balancer is selected', () => { |
| 62 | expect( |
| 63 | getApiEndpoint({ |
| 64 | selectedDatabaseId: 'load-balancer', |
| 65 | projectRef: 'project-ref', |
| 66 | resolvedEndpoint: 'https://project-ref.supabase.co', |
| 67 | loadBalancers: [makeLoadBalancer('https://lb.supabase.co') as LoadBalancer], |
| 68 | selectedDatabase: undefined, |
| 69 | }) |
| 70 | ).toBe('https://lb.supabase.co/rest/v1/') |
| 71 | }) |
| 72 | |
| 73 | it('returns empty string when load balancer is selected but none exist', () => { |
| 74 | expect( |
| 75 | getApiEndpoint({ |
| 76 | selectedDatabaseId: 'load-balancer', |
| 77 | projectRef: 'project-ref', |
| 78 | resolvedEndpoint: 'https://project-ref.supabase.co', |
| 79 | loadBalancers: undefined, |
| 80 | selectedDatabase: undefined, |
| 81 | }) |
| 82 | ).toBe('') |
| 83 | }) |
| 84 | |
| 85 | it('returns database restUrl for a replica database selection', () => { |
| 86 | expect( |
| 87 | getApiEndpoint({ |
| 88 | selectedDatabaseId: 'replica-2', |
| 89 | projectRef: 'project-ref', |
| 90 | resolvedEndpoint: 'https://project-ref.supabase.co', |
| 91 | loadBalancers: undefined, |
| 92 | selectedDatabase: makeDatabase( |
| 93 | 'replica-2', |
| 94 | 'https://replica-2.supabase.co/rest/v1/' |
| 95 | ) as Database, |
| 96 | }) |
| 97 | ).toBe('https://replica-2.supabase.co/rest/v1/') |
| 98 | }) |
| 99 | }) |
| 100 | |
| 101 | describe('buildEntityMaps', () => { |
| 102 | it('returns empty maps for undefined paths', () => { |
| 103 | expect(buildEntityMaps(undefined)).toEqual({ resources: {}, rpcs: {} }) |
| 104 | }) |
| 105 | |
| 106 | it('returns empty maps for empty paths', () => { |
| 107 | expect(buildEntityMaps({})).toEqual({ resources: {}, rpcs: {} }) |
| 108 | }) |
| 109 | |
| 110 | it('skips the root path', () => { |
| 111 | const paths: ProjectJsonSchemaPaths = { |
| 112 | '/': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 113 | } |
| 114 | expect(buildEntityMaps(paths)).toEqual({ resources: {}, rpcs: {} }) |
| 115 | }) |
| 116 | |
| 117 | it('classifies non-rpc paths as resources', () => { |
| 118 | const paths: ProjectJsonSchemaPaths = { |
| 119 | '/users': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 120 | '/posts': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 121 | } |
| 122 | const result = buildEntityMaps(paths) |
| 123 | expect(Object.keys(result.resources)).toEqual(['users', 'posts']) |
| 124 | expect(result.rpcs).toEqual({}) |
| 125 | }) |
| 126 | |
| 127 | it('classifies rpc/ paths as rpcs', () => { |
| 128 | const paths: ProjectJsonSchemaPaths = { |
| 129 | '/rpc/my_function': { post: {} as ProjectJsonSchemaPaths[string]['post'] }, |
| 130 | } |
| 131 | const result = buildEntityMaps(paths) |
| 132 | expect(result.resources).toEqual({}) |
| 133 | expect(Object.keys(result.rpcs)).toEqual(['my_function']) |
| 134 | }) |
| 135 | |
| 136 | it('enriches entities with displayName and camelCase', () => { |
| 137 | const paths: ProjectJsonSchemaPaths = { |
| 138 | '/user_profiles': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 139 | '/rpc/get_user_count': { post: {} as ProjectJsonSchemaPaths[string]['post'] }, |
| 140 | } |
| 141 | const result = buildEntityMaps(paths) |
| 142 | |
| 143 | expect(result.resources['user_profiles']).toEqual({ |
| 144 | id: 'user_profiles', |
| 145 | displayName: 'user profiles', |
| 146 | camelCase: 'userProfiles', |
| 147 | }) |
| 148 | expect(result.rpcs['get_user_count']).toEqual({ |
| 149 | id: 'get_user_count', |
| 150 | displayName: 'get user count', |
| 151 | camelCase: 'getUserCount', |
| 152 | }) |
| 153 | }) |
| 154 | |
| 155 | it('handles mixed resources and rpcs', () => { |
| 156 | const paths: ProjectJsonSchemaPaths = { |
| 157 | '/': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 158 | '/users': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 159 | '/rpc/hello': { post: {} as ProjectJsonSchemaPaths[string]['post'] }, |
| 160 | '/posts': { get: {} as ProjectJsonSchemaPaths[string]['get'] }, |
| 161 | '/rpc/goodbye': { post: {} as ProjectJsonSchemaPaths[string]['post'] }, |
| 162 | } |
| 163 | const result = buildEntityMaps(paths) |
| 164 | expect(Object.keys(result.resources)).toEqual(['users', 'posts']) |
| 165 | expect(Object.keys(result.rpcs)).toEqual(['hello', 'goodbye']) |
| 166 | }) |
| 167 | }) |