4.4 KiB
Development
Prerequisites
- JDK 21 (Temurin recommended)
- MongoDB reachable at
MONGODB_HOST:MONGODB_PORT - Maven is not required — use the bundled wrapper (
./mvnw)
Setup
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
./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/:
| 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:
@SpringBootTest
@EnabledIfEnvironmentVariable(named = "RUN_INTEGRATION_TESTS", matches = "true")
so they are skipped unless you opt in:
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:
PostingTaskServiceJwtServiceSocialMediaCredentialsService
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:
@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
modeldocument directly from a controller — map it to adto. - Throw domain exceptions from
exception/and letGlobalExceptionHandlerrender 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:
- Add
rss.<source>.urltoapplication.properties. - Implement
ParserService— return a uniquegetSourceName()and implementparseAndSaveRssFeed(). Model it onKursivParserService. - Annotate the scheduled entry point with
@Scheduled, matching the cadence of comparable sources (5 min for high-volume, 30 min otherwise). - Add a unit test alongside
KursivParserServiceTest. - Add a log level line in
application.properties. - Document the source in rss-parsers.md.
Adding an endpoint
- Define request/response DTOs in
dto/with Bean Validation annotations. - Add the handler to the relevant controller; extract the caller with
jwtService.extractUserIdFromHeader(authHeader). - Put the logic in a service; add a repository method if persistence is needed.
- Add unit tests for the service.
- Document it under
docs/api/and update that index.
Adding configuration
See configuration.md. In short:
environment placeholder, no default for secrets, and update .env.example.
Before you push
./mvnw clean verify
git status # nothing unexpected staged
Pushing to main deploys to production — deployment.md.