routstrd 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +44 -2
- package/dist/daemon/index.js +62630 -19364
- package/dist/index.js +86 -78
- package/package.json +2 -2
- package/src/daemon/http/usage-summary.test.ts +27 -14
- package/src/tui/usage/app.ts +84 -68
- package/src/tui/usage/data.ts +0 -12
- package/src/tui/usage/render.ts +13 -6
- package/say_hi.sh +0 -32
package/dist/index.js
CHANGED
|
@@ -13876,9 +13876,6 @@ var init_constants = __esm(() => {
|
|
|
13876
13876
|
// src/tui/usage/data.ts
|
|
13877
13877
|
async function fetchStatus() {
|
|
13878
13878
|
try {
|
|
13879
|
-
const running = await isDaemonRunning();
|
|
13880
|
-
if (!running)
|
|
13881
|
-
return null;
|
|
13882
13879
|
const result = await callDaemon("/status");
|
|
13883
13880
|
if (result.error)
|
|
13884
13881
|
return null;
|
|
@@ -13895,9 +13892,6 @@ async function fetchStatus() {
|
|
|
13895
13892
|
}
|
|
13896
13893
|
async function fetchBalance() {
|
|
13897
13894
|
try {
|
|
13898
|
-
const running = await isDaemonRunning();
|
|
13899
|
-
if (!running)
|
|
13900
|
-
return null;
|
|
13901
13895
|
const result = await callDaemon("/keys/balance");
|
|
13902
13896
|
if (result.error)
|
|
13903
13897
|
return null;
|
|
@@ -13914,9 +13908,6 @@ async function fetchBalance() {
|
|
|
13914
13908
|
}
|
|
13915
13909
|
async function fetchUsageSummary() {
|
|
13916
13910
|
try {
|
|
13917
|
-
const running = await isDaemonRunning();
|
|
13918
|
-
if (!running)
|
|
13919
|
-
return null;
|
|
13920
13911
|
const tz = new Date().getTimezoneOffset();
|
|
13921
13912
|
const result = await callDaemon(`/usage/summary?tz=${tz}`);
|
|
13922
13913
|
if (result.error)
|
|
@@ -13950,9 +13941,6 @@ function formatNumber(n) {
|
|
|
13950
13941
|
}
|
|
13951
13942
|
async function fetchClients() {
|
|
13952
13943
|
try {
|
|
13953
|
-
const running = await isDaemonRunning();
|
|
13954
|
-
if (!running)
|
|
13955
|
-
return [];
|
|
13956
13944
|
const result = await callDaemon("/clients");
|
|
13957
13945
|
if (result.error)
|
|
13958
13946
|
return [];
|
|
@@ -14305,13 +14293,22 @@ function renderToday(stats, width) {
|
|
|
14305
14293
|
const currentHour = new Date().getHours();
|
|
14306
14294
|
const _now = new Date;
|
|
14307
14295
|
const todayDateStr = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
|
|
14308
|
-
let todayStats
|
|
14309
|
-
|
|
14310
|
-
|
|
14296
|
+
let todayStats = {
|
|
14297
|
+
date: todayDateStr,
|
|
14298
|
+
requests: 0,
|
|
14299
|
+
satsCost: 0,
|
|
14300
|
+
promptTokens: 0,
|
|
14301
|
+
completionTokens: 0,
|
|
14302
|
+
totalTokens: 0
|
|
14303
|
+
};
|
|
14304
|
+
let recentDays = [];
|
|
14305
|
+
let hourlyMap = new Map;
|
|
14311
14306
|
if (stats.summary) {
|
|
14312
14307
|
const { days, hoursToday } = stats.summary;
|
|
14313
14308
|
const todayDayStat = days[0]?.date === todayDateStr ? days[0] : undefined;
|
|
14314
|
-
|
|
14309
|
+
if (todayDayStat) {
|
|
14310
|
+
todayStats = { date: todayDayStat.date, requests: todayDayStat.requests, satsCost: todayDayStat.satsCost, promptTokens: todayDayStat.promptTokens, completionTokens: todayDayStat.completionTokens, totalTokens: todayDayStat.totalTokens };
|
|
14311
|
+
}
|
|
14315
14312
|
recentDays = days.filter((d) => d.date !== todayDateStr).slice(0, 6);
|
|
14316
14313
|
hourlyMap = new Map(hoursToday.map((h) => [h.hour, { requests: h.requests, satsCost: h.satsCost }]));
|
|
14317
14314
|
}
|
|
@@ -14606,10 +14603,9 @@ async function runUsageTui() {
|
|
|
14606
14603
|
let clients = [];
|
|
14607
14604
|
let visibleTabs = getVisibleTabs(false);
|
|
14608
14605
|
let refreshInterval = null;
|
|
14609
|
-
let shouldUpdate = true;
|
|
14610
14606
|
let autoRefresh = true;
|
|
14611
14607
|
let cleanedUp = false;
|
|
14612
|
-
let
|
|
14608
|
+
let fetching = false;
|
|
14613
14609
|
if (isInteractive) {
|
|
14614
14610
|
stdout.write(enterAlternateScreen() + hideCursor());
|
|
14615
14611
|
stdin.setRawMode?.(true);
|
|
@@ -14633,55 +14629,70 @@ async function runUsageTui() {
|
|
|
14633
14629
|
}
|
|
14634
14630
|
process.on("SIGINT", () => cleanup(0));
|
|
14635
14631
|
process.on("SIGTERM", () => cleanup(0));
|
|
14636
|
-
async function
|
|
14637
|
-
if (
|
|
14632
|
+
async function fetchData() {
|
|
14633
|
+
if (fetching)
|
|
14638
14634
|
return;
|
|
14639
|
-
|
|
14635
|
+
fetching = true;
|
|
14640
14636
|
try {
|
|
14641
|
-
const
|
|
14642
|
-
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14637
|
+
const running2 = await isDaemonRunning();
|
|
14638
|
+
if (!running2) {
|
|
14639
|
+
stats = null;
|
|
14640
|
+
} else {
|
|
14641
|
+
const [newStats, newBalance, newStatus, newClients] = await Promise.all([
|
|
14642
|
+
fetchUsageSummary(),
|
|
14643
|
+
fetchBalance(),
|
|
14644
|
+
fetchStatus(),
|
|
14645
|
+
fetchClients()
|
|
14646
|
+
]);
|
|
14647
|
+
if (newStats)
|
|
14648
|
+
stats = newStats;
|
|
14649
|
+
if (newBalance)
|
|
14650
|
+
balance = newBalance;
|
|
14651
|
+
if (newStatus)
|
|
14652
|
+
status = newStatus;
|
|
14653
|
+
if (newClients && newClients.length > 0)
|
|
14654
|
+
clients = newClients;
|
|
14648
14655
|
const npubsVisible = hasAnyNpubs(clients);
|
|
14649
14656
|
visibleTabs = getVisibleTabs(npubsVisible);
|
|
14650
14657
|
if (currentTab === "npubs" && !npubsVisible) {
|
|
14651
14658
|
currentTab = "clients";
|
|
14652
14659
|
vimState.scrollPos = 0;
|
|
14653
14660
|
}
|
|
14654
|
-
shouldUpdate = false;
|
|
14655
14661
|
}
|
|
14656
|
-
|
|
14657
|
-
|
|
14662
|
+
render2();
|
|
14663
|
+
} finally {
|
|
14664
|
+
fetching = false;
|
|
14665
|
+
}
|
|
14666
|
+
}
|
|
14667
|
+
function render2() {
|
|
14668
|
+
const width = getWidth();
|
|
14669
|
+
const height = getHeight();
|
|
14670
|
+
if (!stats) {
|
|
14671
|
+
stdout.write(moveCursor(1, 1) + eraseDown() + `${COLORS.red}Error: Could not fetch usage data.${COLORS.reset}
|
|
14658
14672
|
` + `Make sure routstrd is running.
|
|
14659
14673
|
` + `
|
|
14660
14674
|
Press Q to quit.`);
|
|
14661
|
-
|
|
14662
|
-
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14675
|
+
return;
|
|
14676
|
+
}
|
|
14677
|
+
const content = renderTabContent(currentTab, stats, balance, status, width, clients);
|
|
14678
|
+
const footer = `${COLORS.dim}Press [Q] to quit, [R] to refresh, [A] to toggle auto-refresh${autoRefresh ? " (on)" : " (off)"} scroll:${vimState.scrollPos}${COLORS.reset}${vimState.mode === "normal" ? ` ${COLORS.yellow}vim: hjkl/arrows, / search, g top, gg bottom${COLORS.reset}` : ""}`;
|
|
14679
|
+
const chrome = renderHeader(currentTab, width, visibleTabs) + renderTabs(currentTab, visibleTabs) + renderSeparator(width) + renderSearchBar();
|
|
14680
|
+
const chromeLines = chrome.split(`
|
|
14667
14681
|
`).length - 1;
|
|
14668
|
-
|
|
14669
|
-
|
|
14682
|
+
const footerSeparator = renderSeparator(width);
|
|
14683
|
+
const footerLines = footerSeparator.split(`
|
|
14670
14684
|
`).length - 1;
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14685
|
+
const contentViewportHeight = Math.max(1, height - chromeLines - footerLines - 1);
|
|
14686
|
+
const visibleContent = applyScrollToContent(content, contentViewportHeight);
|
|
14687
|
+
const footerBlock = (visibleContent ? `
|
|
14674
14688
|
` : "") + footerSeparator + footer;
|
|
14675
|
-
|
|
14676
|
-
} finally {
|
|
14677
|
-
rendering = false;
|
|
14678
|
-
}
|
|
14689
|
+
stdout.write(moveCursor(1, 1) + eraseDown() + chrome + visibleContent + footerBlock);
|
|
14679
14690
|
}
|
|
14680
14691
|
const handleKey = (key) => {
|
|
14681
14692
|
if (vimState.isSearching) {
|
|
14682
14693
|
if (key === "\x1B" || key === "\x1B[3~") {
|
|
14683
14694
|
exitSearchMode();
|
|
14684
|
-
render2(
|
|
14695
|
+
render2();
|
|
14685
14696
|
return;
|
|
14686
14697
|
}
|
|
14687
14698
|
if (key === "\r" || key === `
|
|
@@ -14689,117 +14700,115 @@ Press Q to quit.`);
|
|
|
14689
14700
|
if (stats?.entries)
|
|
14690
14701
|
performSearch(vimState.searchQuery, stats.entries);
|
|
14691
14702
|
exitSearchMode();
|
|
14692
|
-
render2(
|
|
14703
|
+
render2();
|
|
14693
14704
|
return;
|
|
14694
14705
|
}
|
|
14695
14706
|
if (key === "\x7F" || key === "\b") {
|
|
14696
14707
|
vimState.searchQuery = vimState.searchQuery.slice(0, -1);
|
|
14697
14708
|
if (stats?.entries)
|
|
14698
14709
|
performSearch(vimState.searchQuery, stats.entries);
|
|
14699
|
-
render2(
|
|
14710
|
+
render2();
|
|
14700
14711
|
return;
|
|
14701
14712
|
}
|
|
14702
14713
|
if (key === "\x03") {
|
|
14703
14714
|
exitSearchMode();
|
|
14704
|
-
render2(
|
|
14715
|
+
render2();
|
|
14705
14716
|
return;
|
|
14706
14717
|
}
|
|
14707
14718
|
if (key.length === 1 && key.charCodeAt(0) >= 32 && key.charCodeAt(0) < 127) {
|
|
14708
14719
|
vimState.searchQuery += key;
|
|
14709
14720
|
if (stats?.entries)
|
|
14710
14721
|
performSearch(vimState.searchQuery, stats.entries);
|
|
14711
|
-
render2(
|
|
14722
|
+
render2();
|
|
14712
14723
|
}
|
|
14713
14724
|
return;
|
|
14714
14725
|
}
|
|
14715
14726
|
if (key === "q" || key === "Q" || key === "\x03")
|
|
14716
14727
|
return cleanup(0);
|
|
14717
14728
|
if (key === "r" || key === "R") {
|
|
14718
|
-
|
|
14719
|
-
render2(true);
|
|
14729
|
+
fetchData();
|
|
14720
14730
|
return;
|
|
14721
14731
|
}
|
|
14722
14732
|
if (key === "a" || key === "A") {
|
|
14723
14733
|
autoRefresh = !autoRefresh;
|
|
14724
|
-
|
|
14725
|
-
render2(false);
|
|
14734
|
+
render2();
|
|
14726
14735
|
return;
|
|
14727
14736
|
}
|
|
14728
14737
|
if (key === "j" || key === "\x1B[B" || key === "\x1BOB") {
|
|
14729
14738
|
scrollDown();
|
|
14730
|
-
render2(
|
|
14739
|
+
render2();
|
|
14731
14740
|
return;
|
|
14732
14741
|
}
|
|
14733
14742
|
if (key === "k" || key === "\x1B[A" || key === "\x1BOA") {
|
|
14734
14743
|
scrollUp();
|
|
14735
|
-
render2(
|
|
14744
|
+
render2();
|
|
14736
14745
|
return;
|
|
14737
14746
|
}
|
|
14738
14747
|
if (key === "l" || key === "\x1B[C" || key === "\x1BOC") {
|
|
14739
14748
|
const currentIdx = visibleTabs.findIndex((t) => t.id === currentTab);
|
|
14740
14749
|
currentTab = visibleTabs[(currentIdx + 1) % visibleTabs.length].id;
|
|
14741
14750
|
vimState.scrollPos = 0;
|
|
14742
|
-
render2(
|
|
14751
|
+
render2();
|
|
14743
14752
|
return;
|
|
14744
14753
|
}
|
|
14745
14754
|
if (key === "h" || key === "\x1B[D" || key === "\x1BOD") {
|
|
14746
14755
|
const currentIdx = visibleTabs.findIndex((t) => t.id === currentTab);
|
|
14747
14756
|
currentTab = visibleTabs[(currentIdx - 1 + visibleTabs.length) % visibleTabs.length].id;
|
|
14748
14757
|
vimState.scrollPos = 0;
|
|
14749
|
-
render2(
|
|
14758
|
+
render2();
|
|
14750
14759
|
return;
|
|
14751
14760
|
}
|
|
14752
14761
|
if (key === "g") {
|
|
14753
14762
|
if (vimState.lastKey === "g" && Date.now() - vimState.lastKeyTime < 300) {
|
|
14754
14763
|
scrollToBottom();
|
|
14755
14764
|
vimState.lastKey = "";
|
|
14756
|
-
render2(
|
|
14765
|
+
render2();
|
|
14757
14766
|
return;
|
|
14758
14767
|
}
|
|
14759
14768
|
vimState.lastKey = "g";
|
|
14760
14769
|
vimState.lastKeyTime = Date.now();
|
|
14761
14770
|
scrollToTop();
|
|
14762
|
-
render2(
|
|
14771
|
+
render2();
|
|
14763
14772
|
return;
|
|
14764
14773
|
}
|
|
14765
14774
|
if (key === "\x02") {
|
|
14766
14775
|
pageUp();
|
|
14767
|
-
render2(
|
|
14776
|
+
render2();
|
|
14768
14777
|
return;
|
|
14769
14778
|
}
|
|
14770
14779
|
if (key === "\x06") {
|
|
14771
14780
|
pageDown();
|
|
14772
|
-
render2(
|
|
14781
|
+
render2();
|
|
14773
14782
|
return;
|
|
14774
14783
|
}
|
|
14775
14784
|
if (key === "\x15") {
|
|
14776
14785
|
scrollUp(10);
|
|
14777
|
-
render2(
|
|
14786
|
+
render2();
|
|
14778
14787
|
return;
|
|
14779
14788
|
}
|
|
14780
14789
|
if (key === "\x04") {
|
|
14781
14790
|
scrollDown(10);
|
|
14782
|
-
render2(
|
|
14791
|
+
render2();
|
|
14783
14792
|
return;
|
|
14784
14793
|
}
|
|
14785
14794
|
if (key === "\x1B[H" || key === "\x1B[1~" || key === "\x1BOH") {
|
|
14786
14795
|
scrollToTop();
|
|
14787
|
-
render2(
|
|
14796
|
+
render2();
|
|
14788
14797
|
return;
|
|
14789
14798
|
}
|
|
14790
14799
|
if (key === "\x1B[F" || key === "\x1B[4~" || key === "\x1BOF") {
|
|
14791
14800
|
scrollToBottom();
|
|
14792
|
-
render2(
|
|
14801
|
+
render2();
|
|
14793
14802
|
return;
|
|
14794
14803
|
}
|
|
14795
14804
|
if (key === "/") {
|
|
14796
14805
|
startSearch(false);
|
|
14797
|
-
render2(
|
|
14806
|
+
render2();
|
|
14798
14807
|
return;
|
|
14799
14808
|
}
|
|
14800
14809
|
if (key === "?") {
|
|
14801
14810
|
startSearch(true);
|
|
14802
|
-
render2(
|
|
14811
|
+
render2();
|
|
14803
14812
|
return;
|
|
14804
14813
|
}
|
|
14805
14814
|
if (key === "n") {
|
|
@@ -14807,7 +14816,7 @@ Press Q to quit.`);
|
|
|
14807
14816
|
prevSearchResult(stats?.entries.length || 0);
|
|
14808
14817
|
else
|
|
14809
14818
|
nextSearchResult(stats?.entries.length || 0);
|
|
14810
|
-
render2(
|
|
14819
|
+
render2();
|
|
14811
14820
|
return;
|
|
14812
14821
|
}
|
|
14813
14822
|
if (key === "N") {
|
|
@@ -14815,28 +14824,27 @@ Press Q to quit.`);
|
|
|
14815
14824
|
nextSearchResult(stats?.entries.length || 0);
|
|
14816
14825
|
else
|
|
14817
14826
|
prevSearchResult(stats?.entries.length || 0);
|
|
14818
|
-
render2(
|
|
14827
|
+
render2();
|
|
14819
14828
|
return;
|
|
14820
14829
|
}
|
|
14821
14830
|
if (key === "\x1B") {
|
|
14822
14831
|
scrollToTop();
|
|
14823
|
-
render2(
|
|
14832
|
+
render2();
|
|
14824
14833
|
return;
|
|
14825
14834
|
}
|
|
14826
14835
|
const tab = visibleTabs.find((t) => t.key === key);
|
|
14827
14836
|
if (tab) {
|
|
14828
14837
|
currentTab = tab.id;
|
|
14829
14838
|
vimState.scrollPos = 0;
|
|
14830
|
-
render2(
|
|
14839
|
+
render2();
|
|
14831
14840
|
}
|
|
14832
14841
|
};
|
|
14833
14842
|
if (isInteractive)
|
|
14834
14843
|
stdin.on("data", handleKey);
|
|
14835
|
-
await
|
|
14844
|
+
await fetchData();
|
|
14836
14845
|
refreshInterval = setInterval(() => {
|
|
14837
14846
|
if (autoRefresh) {
|
|
14838
|
-
|
|
14839
|
-
render2(true);
|
|
14847
|
+
fetchData();
|
|
14840
14848
|
}
|
|
14841
14849
|
}, 2000);
|
|
14842
14850
|
}
|
|
@@ -15588,7 +15596,7 @@ async function isCocodInstalled(cocodPath) {
|
|
|
15588
15596
|
// package.json
|
|
15589
15597
|
var package_default = {
|
|
15590
15598
|
name: "routstrd",
|
|
15591
|
-
version: "0.3.
|
|
15599
|
+
version: "0.3.5",
|
|
15592
15600
|
module: "src/index.ts",
|
|
15593
15601
|
type: "module",
|
|
15594
15602
|
private: false,
|
|
@@ -15612,7 +15620,7 @@ var package_default = {
|
|
|
15612
15620
|
},
|
|
15613
15621
|
dependencies: {
|
|
15614
15622
|
"@cashu/cashu-ts": "^4.3.0",
|
|
15615
|
-
"@routstr/sdk": "^0.3.
|
|
15623
|
+
"@routstr/sdk": "^0.3.14",
|
|
15616
15624
|
"applesauce-core": "^5.1.0",
|
|
15617
15625
|
"applesauce-relay": "^5.1.0",
|
|
15618
15626
|
"applesauce-wallet-connect": "^6.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "routstrd",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"module": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@cashu/cashu-ts": "^4.3.0",
|
|
27
|
-
"@routstr/sdk": "^0.3.
|
|
27
|
+
"@routstr/sdk": "^0.3.14",
|
|
28
28
|
"applesauce-core": "^5.1.0",
|
|
29
29
|
"applesauce-relay": "^5.1.0",
|
|
30
30
|
"applesauce-wallet-connect": "^6.0.0",
|
|
@@ -6,15 +6,27 @@ import { getUsageSummary, __resetUsageSummaryCacheForTest } from "./usage-summar
|
|
|
6
6
|
|
|
7
7
|
// ─── Test fixtures ────────────────────────────────────────────────────────────
|
|
8
8
|
//
|
|
9
|
-
// All timestamps
|
|
9
|
+
// All timestamps are computed relative to "now" so the fixtures never age
|
|
10
|
+
// out of getUsageSummary's 30-day rolling window. We anchor to a recent UTC
|
|
11
|
+
// midnight ~10 days ago and keep the same hour-of-day structure the
|
|
12
|
+
// tz-bucketing assertions depend on.
|
|
13
|
+
//
|
|
10
14
|
// tz = 300 (UTC-5 / EST). After shifting by -300min, UTC dates become:
|
|
11
|
-
// d1:
|
|
12
|
-
// d2:
|
|
13
|
-
// d3:
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
15
|
+
// d1: <anchor> 10:00Z → local <anchor> 05:00 → local day "<anchor>"
|
|
16
|
+
// d2: <anchor+1d> 03:00Z → local <anchor> 22:00 → local day "<anchor>"
|
|
17
|
+
// d3: <anchor+1d> 06:00Z → local <anchor+1d> 01:00 → local day "<anchor+1d>"
|
|
18
|
+
|
|
19
|
+
const _BASE_UTC_MIDNIGHT = Math.floor(Date.now() / 86_400_000) * 86_400_000;
|
|
20
|
+
const _ANCHOR_MS = _BASE_UTC_MIDNIGHT - 10 * 86_400_000; // 10 days ago UTC
|
|
21
|
+
const D1 = _ANCHOR_MS + 10 * 3_600_000; // 10:00Z → local day <anchor>
|
|
22
|
+
const D2 = _ANCHOR_MS + 1 * 86_400_000 + 3 * 3_600_000; // 03:00Z next day → local day <anchor>
|
|
23
|
+
const D3 = _ANCHOR_MS + 1 * 86_400_000 + 6 * 3_600_000; // 06:00Z next day → local day <anchor+1d>
|
|
24
|
+
|
|
25
|
+
/** Compute the local-day date string the same way the SDK's day grouping does. */
|
|
26
|
+
function localDayDateStr(ts: number, tzOffsetMinutes: number): string {
|
|
27
|
+
const d = new Date(ts - tzOffsetMinutes * 60_000);
|
|
28
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
|
|
29
|
+
}
|
|
18
30
|
|
|
19
31
|
const BASE_ENTRY: Omit<UsageTrackingEntry, "id" | "timestamp" | "modelId" | "client" | "totalTokens"> = {
|
|
20
32
|
baseUrl: "https://api.openai.com/",
|
|
@@ -152,23 +164,24 @@ describe("getUsageSummary", () => {
|
|
|
152
164
|
const summary = await getUsageSummary(driver, CLIENTS, TZ);
|
|
153
165
|
const { days } = summary;
|
|
154
166
|
|
|
155
|
-
// D1 (10:00Z)
|
|
156
|
-
//
|
|
167
|
+
// D1 (10:00Z) and D2 (next day 03:00Z) both bucket to local day <anchor>;
|
|
168
|
+
// D3 (next day 06:00Z) buckets to local day <anchor+1d>.
|
|
169
|
+
// So: day <anchor> has e1+e2, day <anchor+1d> has e3+e4
|
|
157
170
|
expect(days.length).toBeGreaterThanOrEqual(2);
|
|
158
171
|
|
|
159
|
-
// Most-recent-first:
|
|
160
|
-
expect(days[0]!.date).toBe(
|
|
172
|
+
// Most-recent-first: <anchor+1d> first
|
|
173
|
+
expect(days[0]!.date).toBe(localDayDateStr(D3, TZ));
|
|
161
174
|
expect(days[0]!.requests).toBe(2); // e3, e4
|
|
162
175
|
expect(days[0]!.satsCost).toBe(8 + 50);
|
|
163
176
|
|
|
164
|
-
expect(days[1]!.date).toBe(
|
|
177
|
+
expect(days[1]!.date).toBe(localDayDateStr(D1, TZ));
|
|
165
178
|
expect(days[1]!.requests).toBe(2); // e1, e2
|
|
166
179
|
expect(days[1]!.satsCost).toBe(10 + 20);
|
|
167
180
|
});
|
|
168
181
|
|
|
169
182
|
it("returns empty hoursToday (test entries are not today)", async () => {
|
|
170
183
|
const summary = await getUsageSummary(driver, CLIENTS, TZ);
|
|
171
|
-
// Test entries are
|
|
184
|
+
// Test entries are ~10 days ago, not today
|
|
172
185
|
expect(summary.hoursToday).toHaveLength(0);
|
|
173
186
|
});
|
|
174
187
|
|