@@ -955,7 +955,7 @@ def test_the_boundary_is_not_counted_twice(self, env):
955955
956956
957957class TestWindowOrder :
958- """窓の並び —— 相手ごとに返す順が違うので、受け取る側で揃える 。"""
958+ """窓の並びと名前 —— 相手ごとに返す順が違い、名前もぶつかりうる 。"""
959959
960960 def _window (self , name : str , minutes : float | None ) -> "object" :
961961 from app .usage import Window
@@ -969,7 +969,7 @@ def test_the_short_window_comes_first(self, env):
969969 weekly = self ._window ("週" , 7 * 24 * 60 )
970970 session = self ._window ("5 時間" , 5 * 60 )
971971
972- assert [w .label for w in usage .ordered ([weekly , session ])] == ["5 時間" , "週" ]
972+ assert [w .label for w in usage .arranged ([weekly , session ])] == ["5 時間" , "週" ]
973973
974974 def test_windows_without_a_length_keep_their_place_at_the_end (self , env ):
975975 """claude は文面から読むので長さを持たない。元の並びのまま末尾へ。"""
@@ -979,7 +979,7 @@ def test_windows_without_a_length_keep_their_place_at_the_end(self, env):
979979 second = self ._window ("week" , None )
980980 short = self ._window ("5 時間" , 5 * 60 )
981981
982- assert [w .label for w in usage .ordered ([first , second , short ])] == [
982+ assert [w .label for w in usage .arranged ([first , second , short ])] == [
983983 "5 時間" , "session" , "week" ,
984984 ]
985985
@@ -1007,3 +1007,76 @@ def test_it_can_be_enabled_without_one(self, env):
10071007
10081008 assert row ["has_credential" ] is False
10091009 assert row ["can_enable" ] is True
1010+
1011+ def test_windows_with_the_same_name_are_told_apart (self , env ):
1012+ """名前は長さから作るので、同じ長さの窓が 2 つあると見分けが付かない
1013+ —— 実測で codex は 7 日の窓を 2 つ返す(別勘定で、値もまったく違う)。
1014+ """
1015+ from app import usage
1016+ from app .usage import Window
1017+
1018+ pair = [
1019+ Window (id = "secondary" , label = "直近 7 日" , used_percent = 33.0 ,
1020+ window_minutes = 7 * 24 * 60 ),
1021+ Window (id = "primary-10080m" , label = "直近 7 日" , used_percent = 0.0 ,
1022+ window_minutes = 7 * 24 * 60 ),
1023+ ]
1024+
1025+ assert [w .label for w in usage .arranged (pair )] == [
1026+ "直近 7 日(secondary)" , "直近 7 日(primary-10080m)" ,
1027+ ]
1028+
1029+ def test_a_name_that_does_not_clash_is_left_alone (self , env ):
1030+ from app import usage
1031+ from app .usage import Window
1032+
1033+ one = [Window (id = "primary" , label = "直近 5 時間" , used_percent = 1.0 ,
1034+ window_minutes = 300 )]
1035+
1036+ assert [w .label for w in usage .arranged (one )] == ["直近 5 時間" ]
1037+
1038+
1039+ class TestAWindowThatStoppedComing :
1040+ """相手が返さなくなった窓は下へ回す。**消さずに、伸びていないことを示す。**"""
1041+
1042+ def test_it_sinks_below_the_live_ones (self , env ):
1043+ import sqlite3
1044+ from datetime import UTC , datetime , timedelta
1045+
1046+ from app import usage_store
1047+
1048+ with make_client (env , ReplyLLM ()):
1049+ # 混ざって大きく上がった古い線(もう伸びない)
1050+ usage_store .save_quota ("codex" , [{"id" : "primary" , "label" : "直近 7 日" ,
1051+ "used_percent" : 5.0 }])
1052+ usage_store .save_quota ("codex" , [{"id" : "primary" , "label" : "直近 7 日" ,
1053+ "used_percent" : 90.0 }])
1054+ old = (datetime .now (UTC ) - timedelta (hours = 2 )).isoformat (timespec = "seconds" )
1055+ with sqlite3 .connect (usage_store .db_path ()) as conn :
1056+ conn .execute ("UPDATE quota_samples SET at = ?" , (old ,))
1057+ # いま伸びている線
1058+ usage_store .save_quota ("codex" , [{"id" : "primary-300m" , "label" : "直近 5 時間" ,
1059+ "used_percent" : 3.0 }])
1060+ trails = usage_store .quota_trail (datetime .now (UTC ) - timedelta (days = 1 ))
1061+
1062+ assert [t ["window_id" ] for t in trails ] == ["primary-300m" , "primary" ]
1063+ assert trails [0 ]["stale" ] is False
1064+ assert trails [1 ]["stale" ] is True
1065+
1066+ def test_the_screen_says_it_is_no_longer_reported (self , env ):
1067+ import sqlite3
1068+ from datetime import UTC , datetime , timedelta
1069+
1070+ from app import usage_store
1071+
1072+ with make_client (env , ReplyLLM ()) as client :
1073+ usage_store .save_quota ("codex" , [{"id" : "primary" , "label" : "直近 7 日" ,
1074+ "used_percent" : 90.0 }])
1075+ old = (datetime .now (UTC ) - timedelta (hours = 2 )).isoformat (timespec = "seconds" )
1076+ with sqlite3 .connect (usage_store .db_path ()) as conn :
1077+ conn .execute ("UPDATE quota_samples SET at = ?" , (old ,))
1078+ usage_store .save_quota ("codex" , [{"id" : "primary-300m" , "label" : "直近 5 時間" ,
1079+ "used_percent" : 3.0 }])
1080+ html = client .get ("/admin/ai" ).text
1081+
1082+ assert "いまは返ってこない窓" in html
0 commit comments