StatusPageBanner.utils.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. import { describe, expect, it } from 'vitest'
  2. import { getRelevantIncidentIds, shouldShowBanner } from './StatusPageBanner.utils'
  3. const noCache = { id: 'no-cache', cache: null } as const
  4. const noRestrictions = {
  5. id: 'no-restrictions',
  6. cache: { affected_regions: null, affects_project_creation: false },
  7. }
  8. const affectsCreation = {
  9. id: 'affects-creation',
  10. cache: { affected_regions: null, affects_project_creation: true },
  11. }
  12. const usEast1Only = {
  13. id: 'us-east-1-only',
  14. cache: { affected_regions: ['us-east-1'], affects_project_creation: false },
  15. }
  16. const usEast1AndCreation = {
  17. id: 'us-east-1-and-creation',
  18. cache: { affected_regions: ['us-east-1'], affects_project_creation: true },
  19. }
  20. const forced = {
  21. id: 'forced',
  22. cache: { affected_regions: ['us-east-1'], affects_project_creation: false, force: true },
  23. }
  24. describe('shouldShowBanner', () => {
  25. describe('no incidents', () => {
  26. it('does not show when there are no incidents', () => {
  27. expect(
  28. shouldShowBanner({ incidents: [], hasProjects: true, userRegions: new Set(['us-east-1']) })
  29. ).toBe(false)
  30. })
  31. })
  32. describe('user has no projects', () => {
  33. it('does not show when cache is absent', () => {
  34. expect(
  35. shouldShowBanner({ incidents: [noCache], hasProjects: false, userRegions: new Set() })
  36. ).toBe(false)
  37. })
  38. it('does not show when affects_project_creation is false', () => {
  39. expect(
  40. shouldShowBanner({
  41. incidents: [noRestrictions],
  42. hasProjects: false,
  43. userRegions: new Set(),
  44. })
  45. ).toBe(false)
  46. })
  47. it('shows when affects_project_creation is true and no region restriction', () => {
  48. expect(
  49. shouldShowBanner({
  50. incidents: [affectsCreation],
  51. hasProjects: false,
  52. userRegions: new Set(),
  53. })
  54. ).toBe(true)
  55. })
  56. it('does not show when affects_project_creation is true but there is a region restriction', () => {
  57. expect(
  58. shouldShowBanner({
  59. incidents: [usEast1AndCreation],
  60. hasProjects: false,
  61. userRegions: new Set(),
  62. })
  63. ).toBe(false)
  64. })
  65. })
  66. describe('user has projects, no region restriction', () => {
  67. it('shows when cache is absent', () => {
  68. expect(
  69. shouldShowBanner({
  70. incidents: [noCache],
  71. hasProjects: true,
  72. userRegions: new Set(['us-east-1']),
  73. })
  74. ).toBe(true)
  75. })
  76. it('shows when affected_regions is null', () => {
  77. expect(
  78. shouldShowBanner({
  79. incidents: [noRestrictions],
  80. hasProjects: true,
  81. userRegions: new Set(['us-east-1']),
  82. })
  83. ).toBe(true)
  84. })
  85. it('shows when affected_regions is an empty array', () => {
  86. expect(
  87. shouldShowBanner({
  88. incidents: [
  89. { id: 'test', cache: { affected_regions: [], affects_project_creation: false } },
  90. ],
  91. hasProjects: true,
  92. userRegions: new Set(['us-east-1']),
  93. })
  94. ).toBe(true)
  95. })
  96. })
  97. describe('user has projects, with region restriction', () => {
  98. it('shows when user has a primary database in an affected region', () => {
  99. expect(
  100. shouldShowBanner({
  101. incidents: [usEast1Only],
  102. hasProjects: true,
  103. userRegions: new Set(['us-east-1']),
  104. })
  105. ).toBe(true)
  106. })
  107. it('shows when user has a read replica in an affected region', () => {
  108. expect(
  109. shouldShowBanner({
  110. incidents: [
  111. {
  112. id: 'test',
  113. cache: { affected_regions: ['eu-west-1'], affects_project_creation: false },
  114. },
  115. ],
  116. hasProjects: true,
  117. userRegions: new Set(['us-east-1', 'eu-west-1']),
  118. })
  119. ).toBe(true)
  120. })
  121. it('shows when one of multiple affected regions matches', () => {
  122. expect(
  123. shouldShowBanner({
  124. incidents: [
  125. {
  126. id: 'test',
  127. cache: {
  128. affected_regions: ['us-east-1', 'ap-southeast-1'],
  129. affects_project_creation: false,
  130. },
  131. },
  132. ],
  133. hasProjects: true,
  134. userRegions: new Set(['ap-southeast-1']),
  135. })
  136. ).toBe(true)
  137. })
  138. it('shows when affected region has inconsistent casing (AI-generated cache)', () => {
  139. expect(
  140. shouldShowBanner({
  141. incidents: [
  142. {
  143. id: 'test',
  144. cache: { affected_regions: ['US-East-1'], affects_project_creation: false },
  145. },
  146. ],
  147. hasProjects: true,
  148. userRegions: new Set(['us-east-1']),
  149. })
  150. ).toBe(true)
  151. })
  152. it('does not show when user has no databases in any affected region', () => {
  153. expect(
  154. shouldShowBanner({
  155. incidents: [usEast1Only],
  156. hasProjects: true,
  157. userRegions: new Set(['eu-west-1', 'ap-southeast-1']),
  158. })
  159. ).toBe(false)
  160. })
  161. it('does not show when user has projects but no databases in affected region, even with affects_project_creation', () => {
  162. expect(
  163. shouldShowBanner({
  164. incidents: [usEast1AndCreation],
  165. hasProjects: true,
  166. userRegions: new Set(['eu-west-1']),
  167. })
  168. ).toBe(false)
  169. })
  170. })
  171. describe('multiple incidents', () => {
  172. it('shows when at least one incident matches even if others do not', () => {
  173. expect(
  174. shouldShowBanner({
  175. incidents: [usEast1Only, noRestrictions],
  176. hasProjects: true,
  177. userRegions: new Set(['eu-west-1']),
  178. })
  179. ).toBe(true)
  180. })
  181. it('does not show when no incident matches', () => {
  182. expect(
  183. shouldShowBanner({
  184. incidents: [usEast1Only, usEast1AndCreation],
  185. hasProjects: true,
  186. userRegions: new Set(['eu-west-1']),
  187. })
  188. ).toBe(false)
  189. })
  190. it('shows when any incident matches for a no-project user via affects_project_creation', () => {
  191. expect(
  192. shouldShowBanner({
  193. incidents: [usEast1Only, affectsCreation],
  194. hasProjects: false,
  195. userRegions: new Set(),
  196. })
  197. ).toBe(true)
  198. })
  199. })
  200. describe('force', () => {
  201. it('shows for a user with no projects regardless of affects_project_creation', () => {
  202. expect(
  203. shouldShowBanner({ incidents: [forced], hasProjects: false, userRegions: new Set() })
  204. ).toBe(true)
  205. })
  206. it('shows for a user whose regions do not overlap with affected_regions', () => {
  207. expect(
  208. shouldShowBanner({
  209. incidents: [forced],
  210. hasProjects: true,
  211. userRegions: new Set(['eu-west-1']),
  212. })
  213. ).toBe(true)
  214. })
  215. it('shows for a user with projects and no regions at all', () => {
  216. expect(
  217. shouldShowBanner({ incidents: [forced], hasProjects: true, userRegions: new Set() })
  218. ).toBe(true)
  219. })
  220. it('shows even when mixed with non-matching non-forced incidents', () => {
  221. expect(
  222. shouldShowBanner({
  223. incidents: [usEast1Only, forced],
  224. hasProjects: false,
  225. userRegions: new Set(),
  226. })
  227. ).toBe(true)
  228. })
  229. })
  230. describe('hasUnknownRegions', () => {
  231. it('shows when regions are unknown and incident has a region restriction', () => {
  232. expect(
  233. shouldShowBanner({
  234. incidents: [usEast1Only],
  235. hasProjects: true,
  236. userRegions: new Set(),
  237. hasUnknownRegions: true,
  238. })
  239. ).toBe(true)
  240. })
  241. it('still applies no-projects check even when regions are unknown', () => {
  242. expect(
  243. shouldShowBanner({
  244. incidents: [usEast1Only],
  245. hasProjects: false,
  246. userRegions: new Set(),
  247. hasUnknownRegions: true,
  248. })
  249. ).toBe(false)
  250. })
  251. it('does not show for affects_project_creation with no projects when there is a region restriction, even when regions are unknown', () => {
  252. expect(
  253. shouldShowBanner({
  254. incidents: [usEast1AndCreation],
  255. hasProjects: false,
  256. userRegions: new Set(),
  257. hasUnknownRegions: true,
  258. })
  259. ).toBe(false)
  260. })
  261. })
  262. })
  263. describe('getRelevantIncidentIds', () => {
  264. it('returns empty array when there are no incidents', () => {
  265. expect(
  266. getRelevantIncidentIds({ incidents: [], hasProjects: true, userRegions: new Set() })
  267. ).toEqual([])
  268. })
  269. it('returns empty array when no incidents are relevant to the user', () => {
  270. expect(
  271. getRelevantIncidentIds({
  272. incidents: [usEast1Only],
  273. hasProjects: true,
  274. userRegions: new Set(['eu-west-1']),
  275. })
  276. ).toEqual([])
  277. })
  278. it('returns the ID of a single relevant incident', () => {
  279. expect(
  280. getRelevantIncidentIds({
  281. incidents: [noRestrictions],
  282. hasProjects: true,
  283. userRegions: new Set(['us-east-1']),
  284. })
  285. ).toEqual(['no-restrictions'])
  286. })
  287. it('returns IDs of all relevant incidents', () => {
  288. const euWest1Only = {
  289. id: 'eu-west-1-only',
  290. cache: { affected_regions: ['eu-west-1'], affects_project_creation: false },
  291. }
  292. const apSoutheast1Only = {
  293. id: 'ap-southeast-1-only',
  294. cache: { affected_regions: ['ap-southeast-1'], affects_project_creation: false },
  295. }
  296. expect(
  297. getRelevantIncidentIds({
  298. incidents: [euWest1Only, apSoutheast1Only],
  299. hasProjects: true,
  300. userRegions: new Set(['eu-west-1', 'ap-southeast-1']),
  301. })
  302. ).toEqual(expect.arrayContaining(['ap-southeast-1-only', 'eu-west-1-only']))
  303. })
  304. it('returns the ID of a relevant incident when affected_regions has inconsistent casing', () => {
  305. expect(
  306. getRelevantIncidentIds({
  307. incidents: [
  308. {
  309. id: 'mixed-case-region',
  310. cache: { affected_regions: ['US-East-1'], affects_project_creation: false },
  311. },
  312. ],
  313. hasProjects: true,
  314. userRegions: new Set(['us-east-1']),
  315. })
  316. ).toEqual(['mixed-case-region'])
  317. })
  318. it('excludes incidents irrelevant to the user from the result', () => {
  319. // User is in eu-west-1; us-east-1-only incident should not be included
  320. expect(
  321. getRelevantIncidentIds({
  322. incidents: [usEast1Only, noRestrictions],
  323. hasProjects: true,
  324. userRegions: new Set(['eu-west-1']),
  325. })
  326. ).toEqual(['no-restrictions'])
  327. })
  328. describe('user has no projects', () => {
  329. it('includes incidents with affects_project_creation', () => {
  330. expect(
  331. getRelevantIncidentIds({
  332. incidents: [affectsCreation],
  333. hasProjects: false,
  334. userRegions: new Set(),
  335. })
  336. ).toEqual(['affects-creation'])
  337. })
  338. it('excludes incidents without affects_project_creation', () => {
  339. expect(
  340. getRelevantIncidentIds({
  341. incidents: [noRestrictions, usEast1Only],
  342. hasProjects: false,
  343. userRegions: new Set(),
  344. })
  345. ).toEqual([])
  346. })
  347. })
  348. describe('hasUnknownRegions', () => {
  349. it('includes region-restricted incidents when regions are unknown', () => {
  350. expect(
  351. getRelevantIncidentIds({
  352. incidents: [usEast1Only],
  353. hasProjects: true,
  354. userRegions: new Set(),
  355. hasUnknownRegions: true,
  356. })
  357. ).toEqual(['us-east-1-only'])
  358. })
  359. it('still excludes incidents for no-project users even when regions are unknown', () => {
  360. expect(
  361. getRelevantIncidentIds({
  362. incidents: [usEast1Only],
  363. hasProjects: false,
  364. userRegions: new Set(),
  365. hasUnknownRegions: true,
  366. })
  367. ).toEqual([])
  368. })
  369. })
  370. })