Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "tests/ptyunit"]
path = tests/ptyunit
url = https://github.com/fissible/ptyunit
127 changes: 127 additions & 0 deletions tests/integration/test-tui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
source tests/ptyunit/assert.sh

ptyunit_require_bash 4 3 # HISTORY[-1] requires bash 4.3+

PTY_INIT=0.4 # give the TUI time to render before sending keys
PTY_TIMEOUT=8

PASSGEN="./passgen"
PTY="python3 tests/ptyunit/pty_run.py"

# ── Basic rendering ────────────────────────────────────────────────────────────

describe "TUI rendering"
test_that "TUI launches and renders password label"
out=$($PTY "$PASSGEN" q)
assert_contains "$out" "Password"

test_that "TUI renders Base64 line"
out=$($PTY "$PASSGEN" q)
assert_contains "$out" "Base64:"

test_that "TUI renders SHA-256 line"
out=$($PTY "$PASSGEN" q)
assert_contains "$out" "SHA-256:"

test_that "TUI renders strength indicator"
out=$($PTY "$PASSGEN" q)
assert_contains "$out" "Strength:"

test_that "TUI renders hotkey hints"
out=$($PTY "$PASSGEN" q)
assert_contains "$out" "new"
assert_contains "$out" "quit"
end_describe

# ── Key handling ───────────────────────────────────────────────────────────────

describe "key handling"
test_that "r key regenerates password"
out=$($PTY "$PASSGEN" r q)
assert_eq "0" "$?"
assert_contains "$out" "Password"

test_that "Enter regenerates password"
out=$($PTY "$PASSGEN" ENTER q)
assert_eq "0" "$?"
assert_contains "$out" "Password"

test_that "Space regenerates password"
out=$($PTY "$PASSGEN" SPACE q)
assert_eq "0" "$?"
assert_contains "$out" "Password"

test_that "+ key increases displayed length"
out=$($PTY "$PASSGEN" + q)
assert_contains "$out" "28" # default 24 + 4

test_that "++ increases length by 8"
out=$($PTY "$PASSGEN" + + q)
assert_contains "$out" "32" # 24 + 4 + 4

test_that "- key decreases displayed length"
out=$($PTY "$PASSGEN" - q)
assert_contains "$out" "20" # 24 - 4

test_that "1 preset sets length to 4"
out=$($PTY "$PASSGEN" 1 q)
assert_contains "$out" "Length: 4"

test_that "8 preset sets length to 64"
out=$($PTY "$PASSGEN" 8 q)
assert_contains "$out" "Length: 64"
end_describe

# ── DB-safe mode ───────────────────────────────────────────────────────────────

describe "DB-safe mode"
test_that "R key shows DB-safe label"
out=$($PTY "$PASSGEN" R q)
assert_contains "$out" "DB-safe"

test_that "s key after R clears DB-safe mode"
out=$($PTY "$PASSGEN" R s q)
# DB-safe label should no longer be in the hotkey/status area after toggle
# (it may still appear in history display; just confirm the mode changed)
assert_eq "0" "$?"
end_describe

# ── History navigation ─────────────────────────────────────────────────────────

describe "history navigation"
test_that "three generations creates history of 3"
out=$($PTY "$PASSGEN" r r r q)
assert_contains "$out" "3/3"

test_that "< key navigates back in history"
out=$($PTY "$PASSGEN" r r r '<' q)
assert_contains "$out" "2/3"

test_that "< < navigates back two steps"
out=$($PTY "$PASSGEN" r r r '<' '<' q)
assert_contains "$out" "1/3"

test_that "> key navigates forward after going back"
out=$($PTY "$PASSGEN" r r r '<' '>' q)
assert_contains "$out" "3/3"
end_describe

# ── Help screen ────────────────────────────────────────────────────────────────

describe "help screen"
test_that "? key opens help screen"
out=$($PTY "$PASSGEN" '?' q)
assert_contains "$out" "Controls:"

test_that "help screen lists key bindings"
out=$($PTY "$PASSGEN" '?' q)
assert_contains "$out" "Generate new password"

test_that "any key dismisses help and returns to main UI"
out=$($PTY "$PASSGEN" '?' SPACE q)
assert_contains "$out" "Password"
assert_contains "$out" "Strength:"
end_describe

ptyunit_test_summary
1 change: 1 addition & 0 deletions tests/ptyunit
Submodule ptyunit added at 072c3b
6 changes: 6 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Run the passgen test suite via ptyunit.
# Usage: bash tests/run.sh [ptyunit run.sh flags]
# e.g. bash tests/run.sh --unit
# bash tests/run.sh --filter cli
exec bash tests/ptyunit/run.sh "$@"
131 changes: 131 additions & 0 deletions tests/unit/test-cli.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
source tests/ptyunit/assert.sh

PASSGEN="./passgen"

# ── Password output ────────────────────────────────────────────────────────────

describe "password output"
test_that "quiet mode outputs exactly one line"
run "$PASSGEN" -q
assert_eq "0" "$status"
assert_eq "1" "$(echo "$output" | wc -l | tr -d ' ')"

test_that "generated password matches requested length"
run "$PASSGEN" -l 32 -q
assert_eq "0" "$status"
assert_eq "32" "${#output}"

test_that "default length is 24"
run "$PASSGEN" -q
assert_eq "24" "${#output}"

test_that "minimum length (4) is respected"
run "$PASSGEN" -l 4 -q
assert_eq "0" "$status"
assert_eq "4" "${#output}"

test_that "maximum length (128) is respected"
run "$PASSGEN" -l 128 -q
assert_eq "0" "$status"
assert_eq "128" "${#output}"

test_that "-n flag suppresses trailing newline"
result=$(./passgen -q -n | wc -c | tr -d ' ')
assert_eq "24" "$result"
end_describe

# ── Character sets ─────────────────────────────────────────────────────────────

describe "character sets"
test_that "default password contains mixed character classes"
run "$PASSGEN" -l 32 -q
assert_match '[a-z]' "$output"
assert_match '[A-Z]' "$output"
assert_match '[0-9]' "$output"

test_that "simple mode produces alphanumeric-only output"
run "$PASSGEN" -s -l 32 -q
assert_eq "0" "$status"
assert_false echo "$output" | grep -q '[^A-Za-z0-9]'

test_that "DB-safe password contains only DB-safe special chars"
run "$PASSGEN" -d -l 32 -q
assert_eq "0" "$status"
assert_false echo "$output" | grep -q '[^A-Za-z0-9_.\-]'

test_that "DB-safe password never starts with - or ."
for i in 1 2 3 4 5; do
run "$PASSGEN" -d -l 24 -q
first="${output:0:1}"
assert_not_eq "-" "$first" "password should not start with -"
assert_not_eq "." "$first" "password should not start with ."
done

test_that "password never starts with - or . in default mode"
for i in 1 2 3 4 5; do
run "$PASSGEN" -l 24 -q
first="${output:0:1}"
assert_not_eq "-" "$first"
assert_not_eq "." "$first"
done
end_describe

# ── Flag conflict: -d vs -s ────────────────────────────────────────────────────
# Bug: flag ordering determines winner. -d should always win over -s since
# DB-safe implies a specific charset requirement.

describe "flag conflicts"
test_that "-d -s: DB-safe flag wins, password contains DB-safe special chars"
run "$PASSGEN" -d -s -l 32 -q
assert_eq "0" "$status"
assert_match '[_.\-]' "$output"

test_that "-s -d: DB-safe flag wins regardless of order"
run "$PASSGEN" -s -d -l 32 -q
assert_eq "0" "$status"
assert_match '[_.\-]' "$output"
end_describe

# ── Error handling ─────────────────────────────────────────────────────────────

describe "error handling"
test_that "-l without a value exits non-zero"
run "$PASSGEN" -l
assert_not_eq "0" "$status"

test_that "length below minimum exits non-zero"
run "$PASSGEN" -l 3 -q
assert_not_eq "0" "$status"

test_that "length above maximum exits non-zero"
run "$PASSGEN" -l 129 -q
assert_not_eq "0" "$status"

test_that "unknown flag exits non-zero"
run "$PASSGEN" --bogus
assert_not_eq "0" "$status"
end_describe

# ── Non-quiet output structure ─────────────────────────────────────────────────

describe "verbose output"
test_that "non-quiet output includes Password label"
run "$PASSGEN" -l 16
assert_eq "0" "$status"
assert_contains "$output" "Password"

test_that "non-quiet output includes Base64 line"
run "$PASSGEN" -l 16
assert_contains "$output" "Base64:"

test_that "non-quiet output includes SHA-256 line"
run "$PASSGEN" -l 16
assert_contains "$output" "SHA-256:"

test_that "DB-safe non-quiet output includes DB-safe label"
run "$PASSGEN" -d -l 16
assert_contains "$output" "DB-safe"
end_describe

ptyunit_test_summary