Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions lib/convert.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,24 @@ _u7_convert() {
json)
local input="$1"
if [[ "$2" != "to" ]]; then
echo "Usage: u7 cv json <input> to yaml [yield <output>]"
echo "Usage: u7 cv json <input> to <yaml|csv> [yield <output>]"
return 1
fi
local to_fmt="$3"
local output="${input%.*}.$to_fmt"
if [[ "$4" == "yield" ]]; then
if [[ -z "$5" ]]; then
echo "Usage: u7 cv json <input> to <yaml|csv> [yield <output>]"
return 1
fi
output="$5"
fi

if [[ ! -f "$input" ]]; then
echo "File not found: $input"
return 1
fi

case "$to_fmt" in
yaml|yml)
if [[ "$_U7_DRY_RUN" == "1" ]]; then
Expand All @@ -183,6 +192,16 @@ _u7_convert() {
yq -P < "$input" > "$output"
fi
;;
csv)
_u7_require jq || return 1
if [[ "$_U7_DRY_RUN" == "1" ]]; then
echo "[dry-run] jq -r '...' < \"$input\" > \"$output\""
else
local tmpfile
tmpfile=$(mktemp "${output}.XXXXXX") || { echo "Error: Failed to create temp file"; return 1; }
jq -r 'if length == 0 then empty else (.[0] | keys_unsorted) as $keys | $keys, map([.[ $keys[] ]])[] | @csv end' < "$input" > "$tmpfile" && mv "$tmpfile" "$output" || { rm -f "$tmpfile"; return 1; }
fi
;;
*) echo "Unsupported conversion: json to $to_fmt" ; return 1 ;;
esac
;;
Expand Down Expand Up @@ -316,7 +335,7 @@ Entities:
files <files...> to archive yield <output> Create archive
image <input> to <format> [yield <output>] Convert image (png/jpg/webp/gif/etc)
video <input> to <format> [yield <output>] Convert video
json <input> to yaml [yield <output>] Convert JSON to YAML
json <input> to <yaml|csv> [yield <output>] Convert JSON to YAML or CSV
yaml <input> to json [yield <output>] Convert YAML to JSON
csv <input> to json [yield <output>] Convert CSV to JSON
case upper to lower on <files...> Rename to lowercase
Expand Down
99 changes: 31 additions & 68 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1022,77 +1022,40 @@ else
((PASSED++))
fi

# ===== rn watch tests =====

# Test: rn watch usage (missing args)
result=$(u7 rn watch 2>&1)
assert_contains "rn watch shows usage on missing args" "Usage" "$result"

# Test: rn watch usage (missing run keyword)
touch watch_target.txt
result=$(u7 rn watch watch_target.txt 2>&1)
assert_contains "rn watch shows usage when run keyword missing" "Usage" "$result"

# Test: rn watch file not found
result=$(u7 rn watch nonexistent_file.txt run echo hi 2>&1)
assert_contains "rn watch reports missing file" "not found" "$result"

# Test: rn watch dry-run
touch watch_target.txt
result=$(u7 -n rn watch watch_target.txt run echo hello 2>&1)
assert_equals "rn watch dry-run output" "[dry-run] watch watch_target.txt and run echo hello on change" "$result"

# Test: rn watch help text includes watch
result=$(u7 rn --help 2>&1)
assert_contains "rn help mentions watch" "watch" "$result"

# ===== 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"
# ===== cv json to csv tests =====

# Test: Convert JSON to CSV
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' > test_cv.json
if command -v jq &>/dev/null; then
u7 cv json test_cv.json to csv yield test_cv.csv >/dev/null 2>&1
if [[ -f "test_cv.csv" ]]; then
result=$(cat test_cv.csv)
assert_contains "cv json to csv works" "Alice" "$result"
else
echo -e "${RED}✗${NC} cv json to csv (file not created)"
((FAILED++))
fi
else
echo -e "${GREEN}✓${NC} cv json to csv (skipped - jq not installed)"
((PASSED++))
fi

# 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: Convert JSON to CSV missing file
result=$(u7 cv json nonexistent.json to csv 2>&1)
assert_contains "cv json to csv reports missing file" "not found" "$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: Convert JSON to unsupported format
result=$(u7 cv json test_cv.json to xml 2>&1)
assert_contains "cv json rejects unsupported format" "Unsupported" "$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"
# Test: Convert JSON to CSV dry-run
if command -v jq &>/dev/null; then
result=$(u7 -n cv json test_cv.json to csv yield dryrun.csv 2>&1)
assert_contains "cv json to csv dry-run" "dry-run" "$result"
else
echo -e "${GREEN}✓${NC} cv json to csv dry-run (skipped - jq not installed)"
((PASSED++))
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test coverage for the new feature is great. To make it even more robust, I recommend adding a test case for handling an empty JSON array ([]) as input. This is an important edge case that should be handled gracefully (e.g., by producing an empty output file).

Here's an example test you could add:

# Test: Convert empty JSON array to CSV
echo '[]' > test_cv_empty.json
if command -v jq &>/dev/null; then
    u7 cv json test_cv_empty.json to csv yield test_cv_empty.csv >/dev/null 2>&1
    if [[ -f "test_cv_empty.csv" && ! -s "test_cv_empty.csv" ]]; then
        echo -e "${GREEN}${NC} cv json to csv with empty array works"
        ((PASSED++))
    else
        echo -e "${RED}${NC} cv json to csv with empty array failed"
        ((FAILED++))
    fi
else
    echo -e "${GREEN}${NC} cv json to csv with empty array (skipped - jq not installed)"
    ((PASSED++))
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test suite for cv json to csv is missing a case for handling an empty JSON array, like []. The current implementation fails on this input. Adding a test for this scenario would have caught the bug and will prevent future regressions.

You could add a test like this:

# Test: Convert empty JSON array to CSV
echo '[]' > test_empty.json
u7 cv json test_empty.json to csv yield test_empty.csv >/dev/null 2>&1
if [[ -f "test_empty.csv" ]]; then
    result=$(cat test_empty.csv)
    assert_equals "cv json to csv with empty array is empty" "" "$result"
else
    echo -e "${RED}${NC} cv json to csv with empty array (file not created)"
    ((FAILED++))
fi


# Cleanup
cd /
Expand Down
Loading