# Development ## Prerequisites - **JDK 21** (Temurin recommended) - **MongoDB** reachable at `MONGODB_HOST:MONGODB_PORT` - Maven is not required — use the bundled wrapper (`./mvnw`) ## Setup ```bash cp .env.example .env $EDITOR .env # fill in REQUIRED values set -a && source .env && set +a ./mvnw spring-boot:run ``` `http://localhost:8080/swagger-ui.html` gives you an interactive client; use the **Authorize** button to supply a JWT. ## Common commands ```bash ./mvnw clean verify # unit tests + coverage gate — run before pushing ./mvnw test # unit tests only ./mvnw clean package # build the jar ./mvnw spring-boot:run # run locally ./mvnw test -Dtest=JwtServiceTest # a single test class ``` Helper scripts in [`scripts/`](../scripts): | Script | Purpose | | --- | --- | | `run-parser.sh` | Preflight checks then start the application | | `smoke-mongodb.sh` | Verify MongoDB reachability and print a connection string | | `smoke-openai-api.sh` | Verify `OPENAI_API_KEY` works against the OpenAI API | | `smoke-research-api.sh` | Exercise the report endpoints against a running instance | ## Testing Two tiers, separated by an environment variable: **Unit tests** run by default. They mock collaborators and never touch the network. This is what CI and `./mvnw verify` execute. **Integration tests** boot the full Spring context and need real MongoDB plus valid credentials. They are annotated: ```java @SpringBootTest @EnabledIfEnvironmentVariable(named = "RUN_INTEGRATION_TESTS", matches = "true") ``` so they are skipped unless you opt in: ```bash RUN_INTEGRATION_TESTS=true ./mvnw verify ``` Surefire additionally excludes the `integration` JUnit tag (see `pom.xml`). ### Coverage gate `verify` fails if line coverage drops below **80%** on any of: - `PostingTaskService` - `JwtService` - `SocialMediaCredentialsService` Report: `target/site/jacoco/index.html`. To extend the gate to another class, add it to the JaCoCo `qg01-coverage-check` rule in `pom.xml`. ### Test credentials Tests must never contain real credentials. Use placeholders that resolve from the environment: ```java @TestPropertySource(properties = { "spring.data.mongodb.host=${MONGODB_HOST:localhost}", "spring.data.mongodb.username=${MONGODB_USERNAME:}" }) ``` ## Conventions - **Java 21**, Lombok for boilerplate (excluded from the packaged jar). - Constructor injection, not field injection — see `ParserManagerService`. - Controllers stay thin: validate, delegate to a service, return a DTO. No business logic and no repository access from a controller. - Never return a `model` document directly from a controller — map it to a `dto`. - Throw domain exceptions from `exception/` and let `GlobalExceptionHandler` render them; do not build error responses by hand. - Long-running AI calls belong on an async executor (`AsyncConfig`), not the request thread. - Existing code mixes Russian and English comments. New comments should be in English. ## Adding an RSS source `ParserManagerService` discovers parsers through Spring, so no registry needs editing: 1. Add `rss..url` to `application.properties`. 2. Implement `ParserService` — return a unique `getSourceName()` and implement `parseAndSaveRssFeed()`. Model it on `KursivParserService`. 3. Annotate the scheduled entry point with `@Scheduled`, matching the cadence of comparable sources (5 min for high-volume, 30 min otherwise). 4. Add a unit test alongside `KursivParserServiceTest`. 5. Add a log level line in `application.properties`. 6. Document the source in [rss-parsers.md](rss-parsers.md). ## Adding an endpoint 1. Define request/response DTOs in `dto/` with Bean Validation annotations. 2. Add the handler to the relevant controller; extract the caller with `jwtService.extractUserIdFromHeader(authHeader)`. 3. Put the logic in a service; add a repository method if persistence is needed. 4. Add unit tests for the service. 5. Document it under [`docs/api/`](api/README.md) and update that index. ## Adding configuration See [configuration.md](configuration.md#adding-a-new-configuration-value). In short: environment placeholder, no default for secrets, and update `.env.example`. ## Before you push ```bash ./mvnw clean verify git status # nothing unexpected staged ``` Pushing to `main` deploys to production — [deployment.md](deployment.md).