-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Showing heartbeat status in top bar. * Fixing login error that shows up during oauth redirect while here * Made heartbeat circle a clickable link
- Loading branch information
1 parent
349a374
commit a30b4d1
Showing
11 changed files
with
302 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { HEARTBEAT_REQUEST, HEARTBEAT_RESPONSE } from "@src/constants"; | ||
|
||
export function heartbeatRequest(): { | ||
type: "HEARTBEAT_REQUEST"; | ||
} { | ||
return { type: HEARTBEAT_REQUEST }; | ||
} | ||
|
||
export function heartbeatResponse(response: Record<string, any>): { | ||
type: "HEARTBEAT_RESPONSE"; | ||
response: Record<string, any>; | ||
} { | ||
return { type: HEARTBEAT_RESPONSE, response }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { HEARTBEAT_RESPONSE } from "@src/constants"; | ||
import { HeartbeatState } from "@src/types"; | ||
|
||
const INITIAL_STATE: HeartbeatState = { | ||
success: true, | ||
response: {}, | ||
}; | ||
|
||
export default function servers( | ||
state: HeartbeatState = INITIAL_STATE, | ||
action: any | ||
): HeartbeatState { | ||
switch (action.type) { | ||
case HEARTBEAT_RESPONSE: { | ||
return action.response; | ||
} | ||
default: { | ||
return state; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import * as actions from "@src/actions/heartbeat"; | ||
import { getClient } from "@src/client"; | ||
import { ActionType, GetStateFn, SagaGen } from "@src/types"; | ||
import { call, put } from "redux-saga/effects"; | ||
|
||
export function* heartbeatRequest( | ||
getState: GetStateFn, | ||
action: ActionType<typeof actions.heartbeatRequest> | ||
): SagaGen { | ||
const response = yield call(queryHeartbeat); | ||
yield put(actions.heartbeatResponse(response)); | ||
} | ||
|
||
async function queryHeartbeat(): Promise<Record<string, any>> { | ||
const client = getClient(); | ||
|
||
try { | ||
const response: Record<string, any> = await client.execute({ | ||
path: "/__heartbeat__", | ||
headers: undefined, | ||
}); | ||
let success = true; | ||
for (let prop in response) { | ||
if (response[prop] === false) { | ||
success = false; | ||
break; | ||
} | ||
} | ||
return { | ||
success, | ||
response, | ||
}; | ||
} catch (ex) { | ||
return { | ||
success: false, | ||
details: ex, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { setClient } from "@src/client"; | ||
import { SessionInfoBar } from "@src/components/SessionInfoBar"; | ||
import { renderWithProvider } from "@test/testUtils"; | ||
import { act, screen, waitFor } from "@testing-library/react"; | ||
|
||
describe("SessionInfoBar component", () => { | ||
const client = { | ||
execute: vi.fn(), | ||
}; | ||
const healthyStr = "Server heartbeat status is healthy"; | ||
const unhealthyStr = "Server heartbeat status IS NOT healthy"; | ||
|
||
beforeAll(() => { | ||
setClient(client); | ||
}); | ||
|
||
afterEach(() => { | ||
vi.clearAllMocks(); | ||
vi.useRealTimers(); | ||
}); | ||
|
||
it("Should show green server status by default and render user/server info as expected, and render again every minute", async () => { | ||
vi.useFakeTimers(); | ||
let fakeDate = new Date(2024, 0, 1); | ||
vi.setSystemTime(fakeDate); | ||
|
||
client.execute.mockResolvedValue({}); | ||
expect(client.execute).toHaveBeenCalledTimes(0); | ||
renderWithProvider(<SessionInfoBar />); | ||
await vi.waitFor(() => { | ||
expect(client.execute).toHaveBeenCalledTimes(2); // 2 due to provider causing re-render in tests | ||
}); | ||
|
||
expect(screen.getByTitle(healthyStr)).toBeDefined(); | ||
expect(screen.getByTitle("Copy authentication header")).toBeDefined(); | ||
expect(screen.getByText("Documentation")).toBeDefined(); | ||
expect(screen.getByText("Logout")).toBeDefined(); | ||
expect(screen.getByText("Anonymous")).toBeDefined(); | ||
|
||
// ensure execute is called every minute for 5 minutes | ||
for (let i = 1; i < 5; i++) { | ||
await vi.advanceTimersByTimeAsync(60100); | ||
await act(async () => { | ||
await vi.waitFor(() => { | ||
expect(client.execute).toHaveBeenCalledTimes(2 + i * 2); | ||
}); | ||
}); | ||
} | ||
}); | ||
|
||
it("Should show green server status when heartbeat returns all true checks", async () => { | ||
client.execute.mockResolvedValue({ | ||
foo: true, | ||
bar: true, | ||
}); | ||
renderWithProvider(<SessionInfoBar />); | ||
await waitFor(() => new Promise(resolve => setTimeout(resolve, 100))); // debounce wait | ||
expect(screen.getByTitle(healthyStr)).toBeDefined(); | ||
}); | ||
|
||
it("Should show failed server status when heartbeat returns any false checks", async () => { | ||
client.execute.mockResolvedValue({ | ||
foo: false, | ||
bar: true, | ||
}); | ||
renderWithProvider(<SessionInfoBar />); | ||
await waitFor(() => new Promise(resolve => setTimeout(resolve, 100))); // debounce wait | ||
expect(client.execute).toHaveBeenCalled(); | ||
expect(screen.getByTitle(unhealthyStr)).toBeDefined(); | ||
}); | ||
|
||
it("Should show failed server status when heartbeat check throws an error", async () => { | ||
client.execute.mockImplementation(() => { | ||
throw new Error("Test error"); | ||
}); | ||
renderWithProvider(<SessionInfoBar />); | ||
await waitFor(() => new Promise(resolve => setTimeout(resolve, 100))); // debounce wait | ||
expect(screen.getByTitle(unhealthyStr)).toBeDefined(); | ||
}); | ||
}); |
Oops, something went wrong.