- antigravity 기반 CLI status adapter 구현 (gemini 대체) - milestone 파일명 통일 (接두어 제거) - agent-ops roadmap/current.md 갱신 - edge/node README 및 설정 문서 업데이트 - e2e smoke 스크립트 개선
469 lines
13 KiB
Go
469 lines
13 KiB
Go
package status
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestAntigravityCheckerRequestsUsageAndModel(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env sh
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'Auto (Antigravity) Stats For Nerds\n'
|
|
printf '0%% used (Limit resets in 24h)\n'
|
|
printf 'Usage limit: 200\n'
|
|
printf 'Usage limits span all sessions and reset daily.\n'
|
|
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf 'Pro ▬▬▬ 2%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.DailyResetTime != "24h" {
|
|
t.Errorf("DailyResetTime: got %q want %q", status.DailyResetTime, "24h")
|
|
}
|
|
if status.Metadata["usage_limit"] != "200" {
|
|
t.Errorf("usage_limit: got %q want %q", status.Metadata["usage_limit"], "200")
|
|
}
|
|
if status.Metadata["used_percent"] != "0%" {
|
|
t.Errorf("used_percent: got %q want %q", status.Metadata["used_percent"], "0%")
|
|
}
|
|
if status.Metadata["daily_label"] != "Daily quota" {
|
|
t.Errorf("daily_label: got %q want %q", status.Metadata["daily_label"], "Daily quota")
|
|
}
|
|
if status.Metadata["model_usage_count"] != "2" {
|
|
t.Errorf("model_usage_count: got %q want %q", status.Metadata["model_usage_count"], "2")
|
|
}
|
|
if status.Metadata["model_usage_0_name"] != "Flash" {
|
|
t.Errorf("model_usage_0_name: got %q want %q", status.Metadata["model_usage_0_name"], "Flash")
|
|
}
|
|
if status.Metadata["model_usage_1_used_percent"] != "2%" {
|
|
t.Errorf("model_usage_1_used_percent: got %q want %q", status.Metadata["model_usage_1_used_percent"], "2%")
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerWaitsForInputReady(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
// Print the banner and non-input-ready workspace line first, then watch
|
|
// stdin for 0.5s. If anything is received before the prompt is rendered,
|
|
// exit 4. Only after the gap do we print the prompt and accept the real
|
|
// /usage command.
|
|
script := `#!/usr/bin/env bash
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf 'Tips for getting started\n'
|
|
printf 'workspace (/directory)\n'
|
|
if IFS= read -r -t 0.5 early; then
|
|
printf 'early input before prompt: %s\n' "$early"
|
|
exit 4
|
|
fi
|
|
printf '? for shortcuts\n'
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'Auto (Antigravity) Stats For Nerds\n'
|
|
printf '0%% used (Limit resets in 24h)\n'
|
|
printf 'Usage limit: 200\n'
|
|
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.DailyResetTime != "24h" {
|
|
t.Errorf("DailyResetTime: got %q want %q", status.DailyResetTime, "24h")
|
|
}
|
|
if status.Metadata["usage_limit"] != "200" {
|
|
t.Errorf("usage_limit: got %q want %q", status.Metadata["usage_limit"], "200")
|
|
}
|
|
if status.Metadata["model_usage_count"] != "1" {
|
|
t.Errorf("model_usage_count: got %q want %q", status.Metadata["model_usage_count"], "1")
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerNoUsageReturnsRawUnparsed(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env sh
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'Auto (Antigravity) Stats For Nerds\n'
|
|
printf 'No API calls have been made in this session.\n'
|
|
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "" {
|
|
t.Errorf("DailyLimit should be empty, got %q", status.DailyLimit)
|
|
}
|
|
if status.WeeklyLimit != "" {
|
|
t.Errorf("WeeklyLimit should be empty, got %q", status.WeeklyLimit)
|
|
}
|
|
if status.RawOutput == "" {
|
|
t.Error("RawOutput should not be empty")
|
|
}
|
|
if status.Metadata["model_usage_count"] != "1" {
|
|
t.Errorf("model_usage_count: got %q want %q", status.Metadata["model_usage_count"], "1")
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerParsesScreenReaderQuotaWithNoAPICalls(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env sh
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf 'workspace (/directory)\n'
|
|
printf 'Auto (Antigravity) quota\n'
|
|
printf '0%% used\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'No API calls have been made in this session.\n'
|
|
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.DailyResetTime != "" {
|
|
t.Errorf("DailyResetTime should be empty, got %q", status.DailyResetTime)
|
|
}
|
|
if status.Metadata["used_percent"] != "0%" {
|
|
t.Errorf("used_percent: got %q want %q", status.Metadata["used_percent"], "0%")
|
|
}
|
|
if status.Metadata["usage_limit"] != "" {
|
|
t.Errorf("usage_limit should be empty, got %q", status.Metadata["usage_limit"])
|
|
}
|
|
if status.Metadata["model_usage_count"] != "1" {
|
|
t.Errorf("model_usage_count: got %q want %q", status.Metadata["model_usage_count"], "1")
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerParsesScreenReaderQuotaWithoutLimit(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env sh
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'Auto (Antigravity) Stats For Nerds\n'
|
|
printf '0%% used (Limit resets in 24h)\n'
|
|
printf 'Usage limits span all sessions.\n'
|
|
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.DailyResetTime != "24h" {
|
|
t.Errorf("DailyResetTime: got %q want %q", status.DailyResetTime, "24h")
|
|
}
|
|
if status.Metadata["used_percent"] != "0%" {
|
|
t.Errorf("used_percent: got %q want %q", status.Metadata["used_percent"], "0%")
|
|
}
|
|
if status.Metadata["usage_limit"] != "" {
|
|
t.Errorf("usage_limit should be empty, got %q", status.Metadata["usage_limit"])
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerModelUsageOptionalFallback(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env sh
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
printf 'Auto (Antigravity) Stats For Nerds\n'
|
|
printf '0%% used (Limit resets in 24h)\n'
|
|
printf 'Usage limit: 200\n'
|
|
|
|
IFS= read -r model_command
|
|
exit 1
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed despite optional /model fallback: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.Metadata["usage_limit"] != "200" {
|
|
t.Errorf("usage_limit: got %q want %q", status.Metadata["usage_limit"], "200")
|
|
}
|
|
if status.Metadata["used_percent"] != "0%" {
|
|
t.Errorf("used_percent: got %q want %q", status.Metadata["used_percent"], "0%")
|
|
}
|
|
if status.Metadata["model_usage_count"] != "" {
|
|
t.Errorf("model_usage_count should be empty, got %q", status.Metadata["model_usage_count"])
|
|
}
|
|
}
|
|
|
|
func TestAntigravityCheckerReadyRegexScope(t *testing.T) {
|
|
dir := t.TempDir()
|
|
fakeAgy := filepath.Join(dir, "agy")
|
|
|
|
script := `#!/usr/bin/env bash
|
|
printf 'Antigravity CLI v0.42.0\n'
|
|
printf 'Auto (Antigravity) quota\n'
|
|
printf '0%% used\n'
|
|
printf '? for shortcuts\n'
|
|
sleep 0.1
|
|
|
|
IFS= read -r command
|
|
cmd="${command#?}"
|
|
if [ "$cmd" != "/usage" ] && [ "$command" != "/usage" ]; then
|
|
printf 'unexpected command: %s\n' "$command"
|
|
exit 3
|
|
fi
|
|
|
|
# Check for early /model command within 0.5s before /usage response is outputted
|
|
if IFS= read -r -t 0.5 early_command; then
|
|
printf 'early model command received before /usage response: %s\n' "$early_command"
|
|
exit 5
|
|
fi
|
|
|
|
# Send the actual /usage response since no early command arrived
|
|
printf 'Usage limit: 200\n'
|
|
printf '0%% used (Limit resets in 24h)\n'
|
|
|
|
# Now read the normal /model command
|
|
IFS= read -r model_command
|
|
model_cmd="${model_command#?}"
|
|
if [ "$model_cmd" != "/model" ] && [ "$model_command" != "/model" ]; then
|
|
printf 'unexpected command: %s\n' "$model_command"
|
|
exit 3
|
|
fi
|
|
printf 'Select Model\n'
|
|
printf 'Model usage\n'
|
|
printf 'Flash ▬▬▬ 0%% Resets: 2:15 PM (24h)\n'
|
|
printf '(Press Esc to close)\n'
|
|
|
|
IFS= read -r exit_command
|
|
pkill -P $$ 2>/dev/null || true
|
|
exit 0
|
|
`
|
|
|
|
if err := os.WriteFile(fakeAgy, []byte(script), 0o755); err != nil {
|
|
t.Fatalf("write fake agy: %v", err)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
checker := NewAntigravityChecker(fakeAgy)
|
|
status, err := checker.Check(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Check failed: %v", err)
|
|
}
|
|
if status.DailyLimit != "100%" {
|
|
t.Errorf("DailyLimit: got %q want %q", status.DailyLimit, "100%")
|
|
}
|
|
if status.DailyResetTime != "24h" {
|
|
t.Errorf("DailyResetTime: got %q want %q", status.DailyResetTime, "24h")
|
|
}
|
|
if status.Metadata["usage_limit"] != "200" {
|
|
t.Errorf("usage_limit: got %q want %q", status.Metadata["usage_limit"], "200")
|
|
}
|
|
if status.Metadata["used_percent"] != "0%" {
|
|
t.Errorf("used_percent: got %q want %q", status.Metadata["used_percent"], "0%")
|
|
}
|
|
if status.Metadata["model_usage_count"] != "1" {
|
|
t.Errorf("model_usage_count: got %q want %q", status.Metadata["model_usage_count"], "1")
|
|
}
|
|
}
|