Skip to content

Commit

Permalink
ci: Add test step to GitHub Actions workflow (#75)
Browse files Browse the repository at this point in the history
* ci: Add test step to GitHub Actions workflow

* setup deno config

* test: Add comprehensive tests for OrderDisplay utility functions

* test: Fix trailing commas in Deno test cases
  • Loading branch information
JohnPhamous authored Feb 17, 2025
1 parent d29ffb8 commit e08e1d3
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 8 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: CLI (Check)

on: [push]
on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
# run format, lint, and test
Expand All @@ -14,3 +18,4 @@ jobs:
- run: deno fmt --check
- run: deno lint
- run: deno check --config deno.json ./src/index.ts
- run: deno test --allow-all
5 changes: 5 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"recommendations": [
"denoland.vscode-deno"
]
}
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"deno.enable": true,
"deno.lint": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno",
"[typescriptreact]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}
}
5 changes: 5 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions src/lib/orders/OrderDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Box, measureElement, Text, useInput } from "ink";
import process from "node:process";
import dayjs from "npm:[email protected]";
import React, { useEffect } from "react";
import { Row } from "../Row.tsx";
import { GPUS_PER_NODE } from "../constants.ts";
import { formatDuration } from "./index.tsx";
import type { HydratedOrder } from "./types.ts";
import process from "node:process";

function orderDetails(order: HydratedOrder) {
export function orderDetails(order: HydratedOrder) {
const duration = dayjs(order.end_at).diff(order.start_at);
const durationInHours = duration === 0 ? 1 : duration / 1000 / 60 / 60;
const pricePerGPUHour = order.price /
Expand All @@ -16,11 +16,9 @@ function orderDetails(order: HydratedOrder) {

const executedPriceDollarsPerGPUHour =
typeof order.execution_price === "number"
? (
order.execution_price / // cents
? order.execution_price / // cents
(order.quantity * GPUS_PER_NODE * durationInHours) / // cents per gpu-hour
100 // dollars per gpu-hour
)
: undefined;

return {
Expand Down Expand Up @@ -339,14 +337,18 @@ export function ScrollArea({
const numberOfOrdersAboveScrollArea = state.scrollTop;
const dateRangeAboveScrollArea = orders.length > 0
? `${formatDateTime(orders[0].start_at)}${
formatDateTime(orders[numberOfOrdersAboveScrollArea - 1]?.end_at || "0")
formatDateTime(
orders[numberOfOrdersAboveScrollArea - 1]?.end_at || "0",
)
}`
: "";
const numberOfOrdersBelowScrollArea = orders.length -
(state.scrollTop + state.height);
const dateRangeBelowScrollArea = orders.length > 0
? `${
formatDateTime(orders[state.scrollTop + state.height]?.start_at || "0")
formatDateTime(
orders[state.scrollTop + state.height]?.start_at || "0",
)
}${formatDateTime(orders[orders.length - 1].end_at)}`
: "";
const canScrollDown = state.scrollTop + state.height < state.innerHeight &&
Expand Down
86 changes: 86 additions & 0 deletions src/lib/orders/__tests__/OrderDisplay.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
import { GPUS_PER_NODE } from "../../constants.ts";
import { orderDetails } from "../OrderDisplay.tsx";
import type { HydratedOrder } from "../types.ts";
import { OrderStatus } from "../types.ts";

const baseOrder: HydratedOrder = {
object: "order",
id: "test-id",
side: "buy",
instance_type: "A100",
price: 10_000, // 100 USD in cents
start_at: "2024-02-20T00:00:00Z",
end_at: "2024-02-20T01:00:00Z", // 1 hour duration
quantity: 1,
flags: {
market: false,
post_only: false,
ioc: false,
prorate: false,
},
created_at: "2024-02-19T00:00:00Z",
executed: false,
cancelled: false,
status: OrderStatus.Open,
};

Deno.test("orderDetails - calculates price per GPU hour correctly", () => {
const result = orderDetails(baseOrder);
// $100 / (1 quantity * 1 hour * GPUS_PER_NODE)
const expectedPricePerGPUHour = 100 / (1 * 1 * GPUS_PER_NODE);
assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour);
});

Deno.test(
"orderDetails - handles zero duration by using 1 hour minimum",
() => {
const zeroOrder = {
...baseOrder,
start_at: "2024-02-20T00:00:00Z",
end_at: "2024-02-20T00:00:00Z",
};
const result = orderDetails(zeroOrder);
const expectedPricePerGPUHour = 100 / (1 * 1 * GPUS_PER_NODE);
assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour);
},
);

Deno.test(
"orderDetails - calculates executed price per GPU hour when available",
() => {
const executedOrder = {
...baseOrder,
executed: true,
execution_price: 8000, // 80 USD in cents
};
const result = orderDetails(executedOrder);
const expectedExecutedPrice = 80 / (1 * 1 * GPUS_PER_NODE);
assertEquals(result.executedPriceDollarsPerGPUHour, expectedExecutedPrice);
},
);

Deno.test(
"orderDetails - returns undefined for executedPriceDollarsPerGPUHour when no execution price",
() => {
const result = orderDetails(baseOrder);
assertEquals(result.executedPriceDollarsPerGPUHour, undefined);
},
);

Deno.test("orderDetails - formats duration correctly", () => {
const result = orderDetails(baseOrder);
assertEquals(result.durationFormatted, "1h"); // Assuming formatDuration returns "1h" for 1 hour
});

Deno.test("orderDetails - handles multiple nodes and longer duration", () => {
const multiNodeOrder = {
...baseOrder,
quantity: 2,
end_at: "2024-02-20T03:00:00Z", // 3 hours duration
};
const result = orderDetails(multiNodeOrder);
// $100 / (2 quantity * 3 hours * GPUS_PER_NODE)
const expectedPricePerGPUHour = 100 / (2 * 3 * GPUS_PER_NODE);
assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour);
});

0 comments on commit e08e1d3

Please sign in to comment.