Skip to content

build: add Spotless, Detekt, Lint baseline, Konsist, pre-commit hook#129

Open
Lemkinator wants to merge 7 commits into
mainfrom
feat/modernize-p2-static-analysis
Open

build: add Spotless, Detekt, Lint baseline, Konsist, pre-commit hook#129
Lemkinator wants to merge 7 commits into
mainfrom
feat/modernize-p2-static-analysis

Conversation

@Lemkinator

Copy link
Copy Markdown
Owner

Summary

Steps 2, 4, 5 of the modernization plan: static analysis tooling, architecture rules, and pre-commit enforcement.

  • Spotless + Detekt — ktlint formatting, Detekt static analysis (config/detekt/detekt.yml), Apache 2.0 license headers stamped repo-wide, IDE↔CLI sync via .idea/ktlint-plugin.xml.
  • Detekt findings: 133 → 0 — mechanical fixes (printStackTraceLog.e, wildcard imports, ktlint naming) plus structural refactors extracting complex Volley response-handling and UI dispatch logic (AddURLActivity, URLActivity, GenerateQRCodeActivity, ProviderInfoBottomSheet, provider getCreateRequest implementations) into named private functions — no behavior changes.
  • Android Lint baseline (app/lint-baseline.xml) — 18 pre-existing warnings grandfathered in, not introduced by this PR.
  • Konsist architecture tests (ArchitectureTest.kt) — enforces data/domain/ui layering (data may depend on domain.model's shared value types, never on use cases), use-case operator invoke, ViewModel inheritance, @HiltViewModel constructor injection. Deliberately plain JUnit5 rather than Kotest — full unit-test scaffolding lands in Plan 4.
  • dependency-analysisbuildHealth wired, report is clean.
  • Pre-commit hook (.githooks/pre-commit) — blocks commits with Spotless/Detekt violations; documented opt-in in README/CLAUDE.md. .gitattributes enforces LF line endings.

Test plan

  • ./gradlew spotlessCheck — green
  • ./gradlew detekt — green (0 findings)
  • ./gradlew lintDebug — green (baseline filters 18 pre-existing warnings)
  • ./gradlew assembleDebug — green
  • ./gradlew test — green (includes Konsist architecture tests)
  • ./gradlew buildHealth — clean report
  • Pre-commit hook verified to fire and block on a formatting violation

🤖 Generated with Claude Code

Lemkinator and others added 7 commits July 25, 2026 20:20
Step 2 of the modernization plan (config/spotless/apache-2.0.kt uses 2023,
this app's first-commit year, as the copyright start).
Stamps Apache 2.0 license headers on Kotlin/XML/gradle sources and applies
ktlint formatting repo-wide (Step 2 of the modernization plan). Also drops
the deprecated AGP lint sarifReport/htmlReport toggles and adds the ktlint
header delimiter regex Spotless 8.8 requires for kotlinGradle/xml targets.
- Replace bare e.printStackTrace() with Log.e(tag, message, e) across
  provider/use-case/UI catch blocks so stack traces reach logcat instead
  of stdout, and suppress TooGenericExceptionCaught with justification
  where narrowing the catch type isn't practical (network/JSON parsing).
- Expand wildcard imports (de.lemke.oneurl.data.database.*, androidx.room.*).
- Rename domainMapper.kt -> DomainMapper.kt (ktlint standard:filename).
- Rename RequestQueueSingleton's INSTANCE -> instance (ktlint property-naming).

Step 2 of the modernization plan.
…ileTree

Root build.gradle.kts and settings.gradle.kts were never linted or
license-checked since app's kotlinGradle target only resolved relative
to app/. Apply Spotless at root with a fileTree(rootDir) covering every
*.gradle.kts in the repo, and drop the now-redundant block from
app/build.gradle.kts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…line

- Extract inline Volley success/error response handling into named
  private functions across provider getCreateRequest implementations
  (L4f, Murl, Oneptco, Shareaholic, Spoome, Ulvis, VgdIsgd) and
  CheckURLSafetyUseCase, resolving CyclomaticComplexMethod/LongMethod.
- Same extraction for AddURLActivity.showErrorDialog,
  GenerateQRCodeActivity.initControls, ProviderInfoBottomSheet.onViewCreated,
  and URLActivity.onOptionsItemSelected/collectState.
- Collapse ShortURLProviderCompanion's class+companion-object wrapper into
  a plain object (UtilityClassWithPublicConstructor).
- Simplify URL.contains()'s multi-field OR chain into a list + any {}
  (ComplexCondition).
- Configure ReturnCount (max 4, excludeGuardClauses) — the four flagged
  functions are idiomatic guard-clause/when-per-branch Android code, not
  actual control-flow smells.
- Snapshot app/lint-baseline.xml (18 pre-existing warnings not introduced
  by this PR).

Resolves all 133 Detekt findings from the initial rollout. Step 2 of the
modernization plan.
Enforces data/domain/ui layering (data may depend on domain.model — the
shared URL/ShortURLProvider value types — but not on use cases or other
domain logic), use-case operator invoke, ViewModel inheritance, and
@hiltviewmodel constructor injection.

Kotest is deliberately deferred to Plan 4 (unit test scaffolding) rather
than pulled in early for this alone — plain JUnit5 + Konsist is sufficient
here and avoids installing test dependencies ahead of the step that
actually consumes them.

dependency-analysis buildHealth report is clean (no unused/misconfigured
dependencies).

Step 4 of the modernization plan.
- .gitattributes enforces LF line endings (CRLF breaks Spotless).
- .githooks/pre-commit blocks commits with Spotless/Detekt violations;
  fails fast if core.autocrlf=true is detected.
- Document the one-time hooksPath opt-in in README.md and CLAUDE.md.

Step 5 of the modernization plan.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 107 files, which is 7 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 822d9b71-0f83-462a-901b-c1a155e69647

📥 Commits

Reviewing files that changed from the base of the PR and between 15a2e82 and 893a0fa.

📒 Files selected for processing (108)
  • .gitattributes
  • .githooks/pre-commit
  • CLAUDE.md
  • README.md
  • app/build.gradle.kts
  • app/lint-baseline.xml
  • app/src/debug/java/de/lemke/oneurl/DebugTools.kt
  • app/src/debug/res/values/leak_canary.xml
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/de/lemke/oneurl/App.kt
  • app/src/main/java/de/lemke/oneurl/PersistenceModule.kt
  • app/src/main/java/de/lemke/oneurl/data/URLRepository.kt
  • app/src/main/java/de/lemke/oneurl/data/UserSettingsRepository.kt
  • app/src/main/java/de/lemke/oneurl/data/database/AppDatabase.kt
  • app/src/main/java/de/lemke/oneurl/data/database/Converters.kt
  • app/src/main/java/de/lemke/oneurl/data/database/DomainMapper.kt
  • app/src/main/java/de/lemke/oneurl/data/database/URLDao.kt
  • app/src/main/java/de/lemke/oneurl/data/database/URLDb.kt
  • app/src/main/java/de/lemke/oneurl/data/database/domainMapper.kt
  • app/src/main/java/de/lemke/oneurl/di/DispatchersModule.kt
  • app/src/main/java/de/lemke/oneurl/domain/AddURLUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/CheckURLSafetyUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/DeleteURLUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/GenerateQRCodeUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/GetURLTitleUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/GetURLUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/GetUserSettingsUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/GetVisitCountUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/ObserveURLsUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/ObserveUserSettingsUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/UpdateURLUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/UpdateUserSettingsUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/generateURL/GenerateURLError.kt
  • app/src/main/java/de/lemke/oneurl/domain/generateURL/GenerateURLResult.kt
  • app/src/main/java/de/lemke/oneurl/domain/generateURL/GenerateURLUseCase.kt
  • app/src/main/java/de/lemke/oneurl/domain/generateURL/RequestQueueSingleton.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Dagd.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Dubco.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Gg.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Kurzelinks.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/L4f.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Lstu.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Murl.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Oneptco.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Onesis.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Owovc.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Shareaholic.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/ShortURLProvider.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Shorturlat.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Shrtlnk.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Spoome.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Tinube.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Tinyurl.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Tly.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Tnyim.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/URL.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Ulvis.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/VgdIsgd.kt
  • app/src/main/java/de/lemke/oneurl/domain/model/Zwsim.kt
  • app/src/main/java/de/lemke/oneurl/ui/AddURLActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/AddURLViewModel.kt
  • app/src/main/java/de/lemke/oneurl/ui/GenerateQRCodeActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/GenerateQRCodeViewModel.kt
  • app/src/main/java/de/lemke/oneurl/ui/HelpActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/MainActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/MainViewModel.kt
  • app/src/main/java/de/lemke/oneurl/ui/ProviderActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/ProviderInfoBottomSheet.kt
  • app/src/main/java/de/lemke/oneurl/ui/ProviderViewModel.kt
  • app/src/main/java/de/lemke/oneurl/ui/QRBottomSheet.kt
  • app/src/main/java/de/lemke/oneurl/ui/URLActivity.kt
  • app/src/main/java/de/lemke/oneurl/ui/URLAdapter.kt
  • app/src/main/java/de/lemke/oneurl/ui/URLViewModel.kt
  • app/src/main/res/drawable/ic_launcher_foreground.xml
  • app/src/main/res/drawable/ic_splash.xml
  • app/src/main/res/drawable/ic_splash_animated.xml
  • app/src/main/res/layout-land/activity_generate_qr_code.xml
  • app/src/main/res/layout/activity_add_url.xml
  • app/src/main/res/layout/activity_generate_qr_code.xml
  • app/src/main/res/layout/activity_help.xml
  • app/src/main/res/layout/activity_main.xml
  • app/src/main/res/layout/activity_provider.xml
  • app/src/main/res/layout/activity_url.xml
  • app/src/main/res/layout/listview_item.xml
  • app/src/main/res/layout/listview_item_provider.xml
  • app/src/main/res/layout/view_provider_info_bottomsheet.xml
  • app/src/main/res/layout/view_qr_bottomsheet.xml
  • app/src/main/res/menu/main.xml
  • app/src/main/res/menu/menu_navigation.xml
  • app/src/main/res/menu/menu_qr.xml
  • app/src/main/res/menu/menu_select.xml
  • app/src/main/res/menu/url_bnv.xml
  • app/src/main/res/menu/url_toolbar.xml
  • app/src/main/res/values-de/strings.xml
  • app/src/main/res/values/colors.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/res/values/values.xml
  • app/src/main/res/xml/file_paths.xml
  • app/src/main/res/xml/meta_oneurl.xml
  • app/src/main/res/xml/preferences.xml
  • app/src/release/java/de/lemke/oneurl/DebugTools.kt
  • app/src/test/java/de/lemke/oneurl/ArchitectureTest.kt
  • build.gradle.kts
  • config/detekt/detekt.yml
  • config/spotless/apache-2.0.kt
  • config/spotless/apache-2.0.xml
  • gradle/libs.versions.toml
  • settings.gradle.kts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/modernize-p2-static-analysis

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Lemkinator

Copy link
Copy Markdown
Owner Author

@greptile-apps

@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires in the full static-analysis and architecture-enforcement stack for the project: Spotless (ktlint formatting), Detekt (static analysis), Android Lint baseline, Konsist architecture tests, and an opt-in pre-commit hook. Alongside the tooling, 133 Detekt findings are resolved through a mix of mechanical fixes (printStackTraceLog.e, wildcard-import removal, ktlint formatting) and structural refactors that extract complex Volley response-handling and UI dispatch logic into named private functions across the provider and UI layers.

  • Tooling integration (build.gradle.kts, app/build.gradle.kts, gradle/libs.versions.toml, config/detekt/detekt.yml): Spotless, Detekt, dependency-analysis, and JUnit 5 platform are wired at both root and app module level; the Detekt config is conservatively tuned (MagicNumber and TooManyFunctions disabled, MaxLineLength=140, common benign exceptions excluded from SwallowedException).
  • Konsist architecture tests (ArchitectureTest.kt): Six rules enforce data/domain/ui layering, operator invoke on use cases, ViewModel parent class, and Hilt constructor injection — all currently green.
  • Mechanical refactors (provider files, AddURLActivity, GenerateQRCodeActivity, URLActivity, CheckURLSafetyUseCase): Long anonymous callbacks replaced with named private functions; ShortURLProviderCompanion converted from a class-with-companion to a top-level object; domainMapper.kt renamed to DomainMapper.kt (PascalCase).

Confidence Score: 4/5

Safe to merge — all changes are formatting, tooling wiring, and mechanical refactors with no behavioral differences; the refactored error-handling dispatch in AddURLActivity is logically equivalent to the original.

The bulk of the diff is license headers, ktlint reformatting, and extracting anonymous callbacks into named private functions — none of which alter runtime behavior. The Konsist tests, Detekt config, and pre-commit hook are additive and the test suite is green. The one design-level concern is that simpleErrorMessageRes uses a when expression with else, opting out of the sealed-class exhaustiveness guarantee that Kotlin would otherwise enforce; new error types added later could silently fall to a generic string rather than triggering a compile error.

Files Needing Attention: AddURLActivity.kt (sealed-class when exhaustiveness) and ArchitectureTest.kt (ViewModel parent check robustness for future AndroidViewModel subclasses).

Important Files Changed

Filename Overview
app/src/test/java/de/lemke/oneurl/ArchitectureTest.kt New Konsist architecture tests enforcing layering, use-case invoke, ViewModel inheritance, and Hilt constructor injection — well-structured and correct; scope initialized at field level which is standard Konsist practice
app/src/main/java/de/lemke/oneurl/ui/AddURLActivity.kt Refactored error dialog logic into named extension functions; simpleErrorMessageRes uses a when expression with an else branch over a sealed class, silently dropping compiler exhaustiveness enforcement
config/detekt/detekt.yml Reasonable Detekt configuration; disables MagicNumber and TooManyFunctions, raises MaxLineLength to 140, excludes common benign exception types from SwallowedException
.githooks/pre-commit Runs spotlessCheck and detekt before commits; includes CRLF guard for Windows users and clear remediation instructions
app/build.gradle.kts Adds Spotless, Detekt, JUnit 5 platform, Konsist test deps, and lint configuration with baseline; useJUnitPlatform() correctly wired for all unit tests
gradle/libs.versions.toml Adds versions and aliases for Detekt, Spotless, Konsist, JUnit Jupiter 6.1.2, dependency-analysis, and ktlint (version-only ref); existing versions unchanged
app/src/main/java/de/lemke/oneurl/domain/model/ShortURLProvider.kt Companion class correctly converted to a top-level object; all callers updated; ktlint-style comment formatting fixes applied throughout
app/src/main/java/de/lemke/oneurl/data/database/DomainMapper.kt Renamed from domainMapper.kt to DomainMapper.kt (PascalCase); license header added; functionally identical
app/src/main/java/de/lemke/oneurl/domain/CheckURLSafetyUseCase.kt Response-parsing logic extracted into parseUrlhausResponse and buildBlacklistedResult; logic is semantically equivalent to the original and improves readability

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[AddURLActivity\nshowErrorDialog] --> B{configureFor}
    B -->|NoInternet| C[configureNoInternet\nopen wireless settings]
    B -->|BlacklistedURL| D[configureBlacklisted\nURLhaus / VirusTotal buttons]
    B -->|ServiceTemporarilyUnavailable| E[configureServiceUnavailable\nMore Information button]
    B -->|Custom| F[configureCustom\nstatus code + message]
    B -->|Unknown| G[configureUnknown\nstatus code or generic text]
    B -->|else| H[simpleErrorMessageRes]
    H --> I[R.string resource\nfor simple data-object errors]
    H -->|else catch-all| J[commonutils_error_unknown]

    style J fill:#ffe0b2,stroke:#e65100
    style B fill:#e3f2fd,stroke:#1565c0
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
app/src/main/java/de/lemke/oneurl/ui/AddURLActivity.kt:246-258
The `when` here is used as an expression (returns `Int`), so Kotlin normally enforces exhaustiveness over sealed classes — but the `else` branch opts out of that guarantee. If a new simple `GenerateURLError` subtype is added in the future, it will silently resolve to a generic "unknown error" string with no compile-time warning. Removing `else` and listing the few cases that "shouldn't" reach here (or using `is GenerateURLError.Unknown`, `is GenerateURLError.Custom`, etc. to absorb them) would restore compiler-enforced exhaustiveness.

```suggestion
    private fun simpleErrorMessageRes(error: GenerateURLError): Int =
        when (error) {
            GenerateURLError.AliasAlreadyExists -> R.string.error_alias_already_exists
            GenerateURLError.URLExistsWithDifferentAlias -> R.string.error_url_already_exists_with_different_alias
            GenerateURLError.InvalidURL -> R.string.error_invalid_url
            GenerateURLError.InvalidAlias -> R.string.error_invalid_alias
            GenerateURLError.InvalidURLOrAlias -> R.string.error_invalid_url_or_alias
            GenerateURLError.InternalServerError -> R.string.error_internal_server_error
            GenerateURLError.ServiceOffline -> R.string.error_service_offline
            GenerateURLError.RateLimitExceeded -> R.string.error_rate_limit_exceeded
            GenerateURLError.DomainNotAllowed -> R.string.error_domain_not_allowed
            // Complex types handled by configureFor; should never reach here
            is GenerateURLError.Unknown,
            is GenerateURLError.Custom,
            is GenerateURLError.BlacklistedURL,
            is GenerateURLError.ServiceTemporarilyUnavailable,
            GenerateURLError.NoInternet,
            -> commonutilsR.string.commonutils_error_unknown
        }
```

### Issue 2 of 2
app/src/test/java/de/lemke/oneurl/ArchitectureTest.kt:63-67
`hasParent { parent -> parent.name == "ViewModel" }` checks the **direct** parent class name. Any ViewModel that extends `AndroidViewModel` (or another intermediate subclass) instead of `ViewModel` directly would fail this test even though it legitimately is a ViewModel. All current ViewModels extend `ViewModel()` directly so the tests pass today, but the rule is brittle for future additions — consider checking `it.hasParentWithName("ViewModel") || it.hasParentWithName("AndroidViewModel")` to future-proof it.

Reviews (1): Last reviewed commit: "build: add .gitattributes, pre-commit ho..." | Re-trigger Greptile

Comment on lines +246 to +258
private fun simpleErrorMessageRes(error: GenerateURLError): Int =
when (error) {
GenerateURLError.AliasAlreadyExists -> R.string.error_alias_already_exists
GenerateURLError.URLExistsWithDifferentAlias -> R.string.error_url_already_exists_with_different_alias
GenerateURLError.InvalidURL -> R.string.error_invalid_url
GenerateURLError.InvalidAlias -> R.string.error_invalid_alias
GenerateURLError.InvalidURLOrAlias -> R.string.error_invalid_url_or_alias
GenerateURLError.InternalServerError -> R.string.error_internal_server_error
GenerateURLError.ServiceOffline -> R.string.error_service_offline
GenerateURLError.RateLimitExceeded -> R.string.error_rate_limit_exceeded
GenerateURLError.DomainNotAllowed -> R.string.error_domain_not_allowed
else -> commonutilsR.string.commonutils_error_unknown
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The when here is used as an expression (returns Int), so Kotlin normally enforces exhaustiveness over sealed classes — but the else branch opts out of that guarantee. If a new simple GenerateURLError subtype is added in the future, it will silently resolve to a generic "unknown error" string with no compile-time warning. Removing else and listing the few cases that "shouldn't" reach here (or using is GenerateURLError.Unknown, is GenerateURLError.Custom, etc. to absorb them) would restore compiler-enforced exhaustiveness.

Suggested change
private fun simpleErrorMessageRes(error: GenerateURLError): Int =
when (error) {
GenerateURLError.AliasAlreadyExists -> R.string.error_alias_already_exists
GenerateURLError.URLExistsWithDifferentAlias -> R.string.error_url_already_exists_with_different_alias
GenerateURLError.InvalidURL -> R.string.error_invalid_url
GenerateURLError.InvalidAlias -> R.string.error_invalid_alias
GenerateURLError.InvalidURLOrAlias -> R.string.error_invalid_url_or_alias
GenerateURLError.InternalServerError -> R.string.error_internal_server_error
GenerateURLError.ServiceOffline -> R.string.error_service_offline
GenerateURLError.RateLimitExceeded -> R.string.error_rate_limit_exceeded
GenerateURLError.DomainNotAllowed -> R.string.error_domain_not_allowed
else -> commonutilsR.string.commonutils_error_unknown
}
private fun simpleErrorMessageRes(error: GenerateURLError): Int =
when (error) {
GenerateURLError.AliasAlreadyExists -> R.string.error_alias_already_exists
GenerateURLError.URLExistsWithDifferentAlias -> R.string.error_url_already_exists_with_different_alias
GenerateURLError.InvalidURL -> R.string.error_invalid_url
GenerateURLError.InvalidAlias -> R.string.error_invalid_alias
GenerateURLError.InvalidURLOrAlias -> R.string.error_invalid_url_or_alias
GenerateURLError.InternalServerError -> R.string.error_internal_server_error
GenerateURLError.ServiceOffline -> R.string.error_service_offline
GenerateURLError.RateLimitExceeded -> R.string.error_rate_limit_exceeded
GenerateURLError.DomainNotAllowed -> R.string.error_domain_not_allowed
// Complex types handled by configureFor; should never reach here
is GenerateURLError.Unknown,
is GenerateURLError.Custom,
is GenerateURLError.BlacklistedURL,
is GenerateURLError.ServiceTemporarilyUnavailable,
GenerateURLError.NoInternet,
-> commonutilsR.string.commonutils_error_unknown
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/de/lemke/oneurl/ui/AddURLActivity.kt
Line: 246-258

Comment:
The `when` here is used as an expression (returns `Int`), so Kotlin normally enforces exhaustiveness over sealed classes — but the `else` branch opts out of that guarantee. If a new simple `GenerateURLError` subtype is added in the future, it will silently resolve to a generic "unknown error" string with no compile-time warning. Removing `else` and listing the few cases that "shouldn't" reach here (or using `is GenerateURLError.Unknown`, `is GenerateURLError.Custom`, etc. to absorb them) would restore compiler-enforced exhaustiveness.

```suggestion
    private fun simpleErrorMessageRes(error: GenerateURLError): Int =
        when (error) {
            GenerateURLError.AliasAlreadyExists -> R.string.error_alias_already_exists
            GenerateURLError.URLExistsWithDifferentAlias -> R.string.error_url_already_exists_with_different_alias
            GenerateURLError.InvalidURL -> R.string.error_invalid_url
            GenerateURLError.InvalidAlias -> R.string.error_invalid_alias
            GenerateURLError.InvalidURLOrAlias -> R.string.error_invalid_url_or_alias
            GenerateURLError.InternalServerError -> R.string.error_internal_server_error
            GenerateURLError.ServiceOffline -> R.string.error_service_offline
            GenerateURLError.RateLimitExceeded -> R.string.error_rate_limit_exceeded
            GenerateURLError.DomainNotAllowed -> R.string.error_domain_not_allowed
            // Complex types handled by configureFor; should never reach here
            is GenerateURLError.Unknown,
            is GenerateURLError.Custom,
            is GenerateURLError.BlacklistedURL,
            is GenerateURLError.ServiceTemporarilyUnavailable,
            GenerateURLError.NoInternet,
            -> commonutilsR.string.commonutils_error_unknown
        }
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Comment on lines +63 to +67
.functions(includeNested = false, includeLocal = false)
.any { it.name == "invoke" && it.hasModifier(KoModifier.OPERATOR) }
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 hasParent { parent -> parent.name == "ViewModel" } checks the direct parent class name. Any ViewModel that extends AndroidViewModel (or another intermediate subclass) instead of ViewModel directly would fail this test even though it legitimately is a ViewModel. All current ViewModels extend ViewModel() directly so the tests pass today, but the rule is brittle for future additions — consider checking it.hasParentWithName("ViewModel") || it.hasParentWithName("AndroidViewModel") to future-proof it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/test/java/de/lemke/oneurl/ArchitectureTest.kt
Line: 63-67

Comment:
`hasParent { parent -> parent.name == "ViewModel" }` checks the **direct** parent class name. Any ViewModel that extends `AndroidViewModel` (or another intermediate subclass) instead of `ViewModel` directly would fail this test even though it legitimately is a ViewModel. All current ViewModels extend `ViewModel()` directly so the tests pass today, but the rule is brittle for future additions — consider checking `it.hasParentWithName("ViewModel") || it.hasParentWithName("AndroidViewModel")` to future-proof it.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

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