diff --git a/lib/completions.sh b/lib/completions.sh index aa5d9c7..ccc7e8d 100644 --- a/lib/completions.sh +++ b/lib/completions.sh @@ -2,7 +2,7 @@ _u7_complete_entities() { local verb="$1" cur="$2" case "$verb" in show|sh) - COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes port usage network git env http docker system definition functions --help" -- "$cur")) + COMPREPLY=($(compgen -W "ip csv json line ssl files diff cpu memory disk processes port usage network git env log http docker system definition functions --help" -- "$cur")) ;; make|mk) COMPREPLY=($(compgen -W "dir file password user copy link archive clone template sequence --help" -- "$cur")) @@ -38,6 +38,7 @@ _u7_complete_args() { env) COMPREPLY=($(compgen -W "match" -- "$cur")) ;; http) COMPREPLY=($(compgen -W "get head headers" -- "$cur")) ;; docker) COMPREPLY=($(compgen -W "containers images volumes networks all" -- "$cur")) ;; + log) COMPREPLY=($(compgen -W "limit match follow" -- "$cur")) ; _filedir ;; *) _filedir ;; esac ;; diff --git a/lib/show.sh b/lib/show.sh index de3bb54..4ed5c65 100644 --- a/lib/show.sh +++ b/lib/show.sh @@ -248,6 +248,45 @@ _u7_show() { esac ;; + log) + local file="$1" + if [[ -z "$file" ]]; then + echo "Usage: u7 sh log [limit N|match |follow]" + return 1 + fi + if [[ ! -f "$file" ]]; then + echo "File not found: $file" + return 1 + fi + case "$2" in + limit) + local n="${3:-20}" + if ! [[ "$n" =~ ^[0-9]+$ ]]; then + echo "Error: limit must be a positive integer, got '$n'" + return 1 + fi + tail -n "$n" "$file" + ;; + match) + if [[ -z "$3" ]]; then + echo "Usage: u7 sh log match " + return 1 + fi + grep -- "$3" "$file" + ;; + follow) + tail -f "$file" + ;; + "") + tail -n 20 "$file" + ;; + *) + echo "Usage: u7 sh log [limit N|match |follow]" + return 1 + ;; + esac + ;; + http) local method="$1" local url="$2" @@ -302,6 +341,7 @@ Entities: network git env [match ] + log [limit N|match |follow] http docker definition of diff --git a/test.sh b/test.sh index d1358ef..a0fcda4 100755 --- a/test.sh +++ b/test.sh @@ -988,6 +988,54 @@ else ((PASSED++)) fi +# ===== sh log tests ===== + +# Setup: create a temp log file +LOG_FILE="$TEST_DIR/test.log" +for i in $(seq 1 30); do + echo "line $i: log entry" >> "$LOG_FILE" +done + +# Test: sh log default shows last 20 lines +result=$(u7 sh log "$LOG_FILE" 2>&1) +line_count=$(echo "$result" | wc -l | tr -d ' ') +assert_equals "sh log default shows 20 lines" "20" "$line_count" +assert_contains "sh log default starts at line 11" "line 11" "$result" + +# Test: sh log with limit +result=$(u7 sh log "$LOG_FILE" limit 5 2>&1) +line_count=$(echo "$result" | wc -l | tr -d ' ') +assert_equals "sh log limit 5 shows 5 lines" "5" "$line_count" +assert_contains "sh log limit 5 starts at line 26" "line 26" "$result" + +# Test: sh log with match +echo "ERROR something broke" >> "$LOG_FILE" +echo "INFO all good" >> "$LOG_FILE" +echo "ERROR another failure" >> "$LOG_FILE" +result=$(u7 sh log "$LOG_FILE" match ERROR 2>&1) +line_count=$(echo "$result" | wc -l | tr -d ' ') +assert_equals "sh log match finds 2 ERROR lines" "2" "$line_count" + +# Test: sh log missing file +result=$(u7 sh log nonexistent.log 2>&1) +assert_contains "sh log reports missing file" "File not found" "$result" + +# Test: sh log no arguments +result=$(u7 sh log 2>&1) +assert_contains "sh log no args shows usage" "Usage" "$result" + +# Test: sh log invalid limit +result=$(u7 sh log "$LOG_FILE" limit abc 2>&1) +assert_contains "sh log rejects non-integer limit" "limit must be a positive integer" "$result" + +# Test: sh log match without pattern +result=$(u7 sh log "$LOG_FILE" match 2>&1) +assert_contains "sh log match without pattern shows usage" "Usage" "$result" + +# Test: sh log unknown subcommand +result=$(u7 sh log "$LOG_FILE" badarg 2>&1) +assert_contains "sh log unknown subcommand shows usage" "Usage" "$result" + # Cleanup cd / rm -rf "$TEST_DIR"