endpoint-status.test.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. import { describe, expect, it } from "vitest";
  2. import {
  3. type EndpointCircuitState,
  4. getEndpointStatusModel,
  5. type IncidentSource,
  6. resolveEndpointDisplayStatus,
  7. } from "@/app/[locale]/settings/providers/_components/endpoint-status";
  8. import { AlertTriangle, Ban, CheckCircle2, HelpCircle, XCircle } from "lucide-react";
  9. describe("getEndpointStatusModel", () => {
  10. const createEndpoint = (lastProbeOk: boolean | null) => ({ lastProbeOk });
  11. describe("Circuit Breaker Priority", () => {
  12. it("should return circuit-open status when circuit is open, regardless of probe", () => {
  13. const endpoint = createEndpoint(true); // Probe is OK
  14. const result = getEndpointStatusModel(endpoint, "open");
  15. expect(result).toEqual({
  16. status: "circuit-open",
  17. labelKey: "settings.providers.endpointStatus.circuitOpen",
  18. severity: "error",
  19. icon: Ban,
  20. color: "text-rose-500",
  21. bgColor: "bg-rose-500/10",
  22. borderColor: "border-rose-500/30",
  23. });
  24. });
  25. it("should return circuit-half-open status when circuit is half-open", () => {
  26. const endpoint = createEndpoint(false); // Probe is bad
  27. const result = getEndpointStatusModel(endpoint, "half-open");
  28. expect(result).toEqual({
  29. status: "circuit-half-open",
  30. labelKey: "settings.providers.endpointStatus.circuitHalfOpen",
  31. severity: "warning",
  32. icon: AlertTriangle,
  33. color: "text-amber-500",
  34. bgColor: "bg-amber-500/10",
  35. borderColor: "border-amber-500/30",
  36. });
  37. });
  38. });
  39. describe("Probe Status Fallback (Circuit Closed or Missing)", () => {
  40. it.each([
  41. { circuit: "closed" as EndpointCircuitState },
  42. { circuit: null },
  43. { circuit: undefined },
  44. ])("should return healthy when probe is ok and circuit is $circuit", ({ circuit }) => {
  45. const endpoint = createEndpoint(true);
  46. const result = getEndpointStatusModel(endpoint, circuit);
  47. expect(result).toEqual({
  48. status: "healthy",
  49. labelKey: "settings.providers.endpointStatus.healthy",
  50. severity: "success",
  51. icon: CheckCircle2,
  52. color: "text-emerald-500",
  53. bgColor: "bg-emerald-500/10",
  54. borderColor: "border-emerald-500/30",
  55. });
  56. });
  57. it.each([
  58. { circuit: "closed" as EndpointCircuitState },
  59. { circuit: null },
  60. { circuit: undefined },
  61. ])("should return unhealthy when probe is failed and circuit is $circuit", ({ circuit }) => {
  62. const endpoint = createEndpoint(false);
  63. const result = getEndpointStatusModel(endpoint, circuit);
  64. expect(result).toEqual({
  65. status: "unhealthy",
  66. labelKey: "settings.providers.endpointStatus.unhealthy",
  67. severity: "error",
  68. icon: XCircle,
  69. color: "text-rose-500",
  70. bgColor: "bg-rose-500/10",
  71. borderColor: "border-rose-500/30",
  72. });
  73. });
  74. it.each([
  75. { circuit: "closed" as EndpointCircuitState },
  76. { circuit: null },
  77. { circuit: undefined },
  78. ])("should return unknown when probe is null and circuit is $circuit", ({ circuit }) => {
  79. const endpoint = createEndpoint(null);
  80. const result = getEndpointStatusModel(endpoint, circuit);
  81. expect(result).toEqual({
  82. status: "unknown",
  83. labelKey: "settings.providers.endpointStatus.unknown",
  84. severity: "neutral",
  85. icon: HelpCircle,
  86. color: "text-slate-400",
  87. bgColor: "bg-slate-400/10",
  88. borderColor: "border-slate-400/30",
  89. });
  90. });
  91. });
  92. });
  93. describe("IncidentSource", () => {
  94. it("should have correct type values", () => {
  95. const source: IncidentSource = "provider";
  96. expect(source).toBe("provider");
  97. const endpointSource: IncidentSource = "endpoint";
  98. expect(endpointSource).toBe("endpoint");
  99. });
  100. });
  101. describe("resolveEndpointDisplayStatus", () => {
  102. const createEndpoint = (lastProbeOk: boolean | null, isEnabled?: boolean) =>
  103. ({ lastProbeOk, isEnabled }) as { lastProbeOk: boolean | null; isEnabled?: boolean };
  104. describe("Priority: circuit-open", () => {
  105. it("should return circuit-open with endpoint source when circuit is open", () => {
  106. const endpoint = createEndpoint(true);
  107. const result = resolveEndpointDisplayStatus(endpoint, "open");
  108. expect(result).toEqual({
  109. status: "circuit-open",
  110. source: "endpoint",
  111. priority: 0,
  112. });
  113. });
  114. it("should return circuit-open even when probe is failed", () => {
  115. const endpoint = createEndpoint(false);
  116. const result = resolveEndpointDisplayStatus(endpoint, "open");
  117. expect(result).toEqual({
  118. status: "circuit-open",
  119. source: "endpoint",
  120. priority: 0,
  121. });
  122. });
  123. });
  124. describe("Priority: circuit-half-open", () => {
  125. it("should return circuit-half-open with endpoint source when circuit is half-open", () => {
  126. const endpoint = createEndpoint(false);
  127. const result = resolveEndpointDisplayStatus(endpoint, "half-open");
  128. expect(result).toEqual({
  129. status: "circuit-half-open",
  130. source: "endpoint",
  131. priority: 1,
  132. });
  133. });
  134. it("should return circuit-half-open even when probe is ok", () => {
  135. const endpoint = createEndpoint(true);
  136. const result = resolveEndpointDisplayStatus(endpoint, "half-open");
  137. expect(result).toEqual({
  138. status: "circuit-half-open",
  139. source: "endpoint",
  140. priority: 1,
  141. });
  142. });
  143. });
  144. describe("Priority: enabled/disabled (circuit-closed)", () => {
  145. it("should return enabled when circuit is closed and endpoint is enabled", () => {
  146. const endpoint = createEndpoint(true, true);
  147. const result = resolveEndpointDisplayStatus(endpoint, "closed");
  148. expect(result).toEqual({
  149. status: "enabled",
  150. source: "provider",
  151. priority: 2,
  152. });
  153. });
  154. it("should return disabled when circuit is closed and endpoint is disabled", () => {
  155. const endpoint = createEndpoint(true, false);
  156. const result = resolveEndpointDisplayStatus(endpoint, "closed");
  157. expect(result).toEqual({
  158. status: "disabled",
  159. source: "provider",
  160. priority: 3,
  161. });
  162. });
  163. it("should return enabled when circuit is closed and isEnabled is undefined", () => {
  164. const endpoint = createEndpoint(true, undefined);
  165. const result = resolveEndpointDisplayStatus(endpoint, "closed");
  166. expect(result).toEqual({
  167. status: "enabled",
  168. source: "provider",
  169. priority: 2,
  170. });
  171. });
  172. it("should return enabled when circuit is closed and isEnabled is null", () => {
  173. const endpoint = createEndpoint(true, null as unknown as undefined);
  174. const result = resolveEndpointDisplayStatus(endpoint, "closed");
  175. expect(result).toEqual({
  176. status: "enabled",
  177. source: "provider",
  178. priority: 2,
  179. });
  180. });
  181. });
  182. describe("Priority ordering", () => {
  183. it("should have circuit-open (0) > circuit-half-open (1) > enabled (2) > disabled (3)", () => {
  184. const endpoint = createEndpoint(true, true);
  185. const openResult = resolveEndpointDisplayStatus(endpoint, "open");
  186. const halfOpenResult = resolveEndpointDisplayStatus(endpoint, "half-open");
  187. const enabledResult = resolveEndpointDisplayStatus(endpoint, "closed");
  188. const disabledEndpoint = createEndpoint(true, false);
  189. const disabledResult = resolveEndpointDisplayStatus(disabledEndpoint, "closed");
  190. expect(openResult.priority).toBe(0);
  191. expect(halfOpenResult.priority).toBe(1);
  192. expect(enabledResult.priority).toBe(2);
  193. expect(disabledResult.priority).toBe(3);
  194. });
  195. });
  196. describe("Null/undefined circuit state", () => {
  197. it("should return enabled when circuit is null and endpoint is enabled", () => {
  198. const endpoint = createEndpoint(true, true);
  199. const result = resolveEndpointDisplayStatus(endpoint, null);
  200. expect(result).toEqual({
  201. status: "enabled",
  202. source: "provider",
  203. priority: 2,
  204. });
  205. });
  206. it("should return enabled when circuit is undefined", () => {
  207. const endpoint = createEndpoint(true, true);
  208. const result = resolveEndpointDisplayStatus(endpoint, undefined);
  209. expect(result).toEqual({
  210. status: "enabled",
  211. source: "provider",
  212. priority: 2,
  213. });
  214. });
  215. });
  216. describe("Edge cases", () => {
  217. it("should handle endpoint without isEnabled property", () => {
  218. const endpoint = { lastProbeOk: true } as { lastProbeOk: boolean | null };
  219. const result = resolveEndpointDisplayStatus(endpoint, "closed");
  220. expect(result).toEqual({
  221. status: "enabled",
  222. source: "provider",
  223. priority: 2,
  224. });
  225. });
  226. it("should return circuit-open when probe is null and circuit is open", () => {
  227. const endpoint = createEndpoint(null);
  228. const result = resolveEndpointDisplayStatus(endpoint, "open");
  229. expect(result).toEqual({
  230. status: "circuit-open",
  231. source: "endpoint",
  232. priority: 0,
  233. });
  234. });
  235. });
  236. });