Skip to content

[CSM-469]: Add unit tests for component - #2

Open
oykukurtgoz wants to merge 2 commits into
masterfrom
test/csm-469
Open

[CSM-469]: Add unit tests for component#2
oykukurtgoz wants to merge 2 commits into
masterfrom
test/csm-469

Conversation

@oykukurtgoz

Copy link
Copy Markdown

Closes CSM-469.

Adds unit tests for main.rb.

The whole PR is 510 added lines and 37 deleted ones; main.rb accounts for 43 added and 37 deleted, and almost all of that is re-indentation of the guarded block. Nothing in the build logic was moved or reworded — the Gradle command, artifact filtering, copying and env-file export are the same statements in the same order.

Changes to main.rb

Three, all driven by the issue's prerequisite refactor and its "replace nil-unsafe access with an explicit error" requirement.

Top-level side effects are guarded. Everything after the four function definitions now sits inside a single if __FILE__ == $PROGRAM_NAME ... end block, with the original statement order kept. Appcircle runs the step with ruby main.rb, so the guard is true there and the body executes exactly as before. require-ing the file only defines get_env_variable, capitalize_first_char, get_gradle_task and run_command.

No require needed a LoadError guard. All four requires (yaml, open3, fileutils, pathname) are stdlib and load in a bare Ruby, so they were left as they are.

AC_ENV_FILE_PATH is validated up front. main.rb used to read ENV['AC_ENV_FILE_PATH'] straight into open at the very end, after Gradle had already run and artifacts had been copied. With the variable missing that raised a bare TypeError; with it empty, an ENOENT. It now goes through get_env_variable alongside the five other required variables, so a missing or empty value aborts before Gradle with Missing env file path. and exit 1. Both before and after, the step fails in that scenario; the difference is that it fails early and says why. Appcircle always sets this variable, so normal runs are unaffected.

Nil-unsafe access raises with context. The tests uncovered three places that crashed with a bare NoMethodError/TypeError on nil or empty input. Each now raises an ArgumentError that names the function and the offending value:

  • capitalize_first_char on nil or "" (previously nil.capitalize)
  • get_gradle_task on variants == nil (previously nil.split)
  • run_command on a nil or blank command (previously system(nil))

None of these are reachable in a normal Appcircle run: AC_VARIANTS is already validated non-empty, and the command string is always composed. The only reachable case is a variant list with an empty segment such as |debug, which failed before and still fails, now with a readable message.

Tests (test/test_main.rb)

Self-executing, 42 examples, no Gemfile / Bundler — rspec gem + Ruby stdlib only. Reuses the ReadableFormatter and coverage report from CSM-230 / CSM-231.

ruby test/test_main.rb

main.rb exposes four functions plus the script as a whole, and the tests cover all five surfaces:

  • get_env_variable, in-process — returns the value when set, returns nil for both a missing key and an empty string, and the || abort(...) guard it is paired with raises SystemExit with status 1.
  • capitalize_first_char, in-process — capitalizes the first character, leaves the rest untouched, mutates the argument in place, handles a single character, and raises ArgumentError for "" and nil.
  • get_gradle_task, in-process — assemble for apk, bundle for aab, assemble fallback for unknown and nil output types, pipe-separated variants joined in order, flavored variants (stagingDebugStagingDebug), nested module paths (feature:login), empty variants returning "", and the error branches: ArgumentError carrying module and output type for nil variants, and for an empty variant segment.
  • run_command, in-process — system is stubbed, so no real command ever runs. Happy path, the echoed @@[command] line, the exact command string forwarded to system, nil and blank commands, and the failure branch: SystemExit with the child's status propagated (a trivial ruby -e 'exit 3' runs so $? is genuinely set), and the command line still being echoed before the exit.
  • The script as a whole, through Open3.capture3(env, "ruby #{MAIN_RB}") — a missing and an empty case for each of AC_MODULE, AC_VARIANTS, AC_OUTPUT_TYPE, AC_REPOSITORY_DIR, AC_OUTPUT_DIR and AC_ENV_FILE_PATH, each asserting exit 1 and the Missing .... message on stderr. Four further cases run with every required variable present and assert the composed command from the @@[command] line: the base cd <repo> && chmod +x ./gradlew && ./gradlew clean app:assembleDebug string, AC_GRADLE_BUILD_EXTRA_ARGS appended, a relative AC_PROJECT_PATH resolved against AC_REPOSITORY_DIR, and an absolute AC_PROJECT_PATH used as-is. The repository directory in those cases is a non-existent path under a tmpdir, so cd fails and ./gradlew is never reached.

No real toolchain, network, or filesystem writes outside the tmpdir.

Coverage

✔  All 42 tests passed
main.rb  41.8%  (23/55 lines)

The number is structural rather than a gap in the tests. Of the 55 executable lines, 23 are the requires and the four function bodies, and those are fully covered in-process. The remaining 32 are the guarded step body: environment validation, Gradle command composition, ./gradlew execution, artifact globbing and copying, and the env-file export. That block only runs in the Open3 subprocesses, which in-process Coverage cannot see. The lines past run_command cannot execute without running Gradle, which the issue rules out.

Raising the figure would mean either extracting the command-composition logic into a pure function (a larger refactor than the issue's "wrap, don't move" prerequisite) or merging subprocess coverage the way CSM-448 does. Both are reasonable follow-ups; neither is in this PR.

Docs

README.md gains a ## Running tests section.

🤖 Generated with Claude Code

oykukurtgoz and others added 2 commits September 7, 2026 23:12
Wrap side-effecting code in if __FILE__ == $PROGRAM_NAME, validate
AC_ENV_FILE_PATH up front, and raise with context on nil/empty input in
capitalize_first_char, get_gradle_task and run_command.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Cover get_env_variable, capitalize_first_char, get_gradle_task and
run_command, plus missing/empty validation for every required env var.
Run with ruby test/test_main.rb; coverage report printed at the end.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 863ff81e-84dd-4e10-ad89-2463d1074e2c


Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant