DataApi.utils.test.ts 5.8 KB

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