Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ jobs:
with:
persist-credentials: false
- name: Setup Java
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: 21
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
with:
languages: ${{ matrix.language }}
queries: security-and-quality
- name: Build for CodeQL
run: mvn -B verify --file pom.xml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4
uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
with:
category: "/language:${{ matrix.language }}"
12 changes: 8 additions & 4 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: 21
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: 21
Expand All @@ -60,6 +60,9 @@ jobs:
verify:
name: Maven Verify (JDK ${{ matrix.java }})
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
Expand All @@ -71,7 +74,7 @@ jobs:
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: ${{ matrix.java }}
Expand Down Expand Up @@ -99,5 +102,6 @@ jobs:
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: target/site/jacoco/jacoco.xml
fail_ci_if_error: false
use_oidc: true
fail_ci_if_error: true
verbose: true
26 changes: 25 additions & 1 deletion .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
with:
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: 21
Expand All @@ -44,3 +44,27 @@ jobs:
name: spotbugs-report
path: target/spotbugsXml.xml
if-no-files-found: ignore

sonarcloud:
name: SonarCloud analysis
if: github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Set up JDK
uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: temurin
java-version: 21
cache: maven
- name: Build and analyze
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: >-
mvn -B verify sonar:sonar
-Dsonar.qualitygate.wait=true
-Dsonar.qualitygate.timeout=300
4 changes: 1 addition & 3 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ on:
- cron: "23 3 * * 1"
workflow_dispatch:

permissions: read-all

jobs:
scorecard:
name: OSSF Scorecards
Expand All @@ -36,7 +34,7 @@ jobs:
results_format: sarif
publish_results: true
- name: Upload Scorecard SARIF
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
with:
sarif_file: scorecard-results.sarif
- name: Upload Scorecard artifact
Expand Down
5 changes: 3 additions & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Curated Java reference implementations for deterministic algorithms and stateful
[![CI](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/maven.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/maven.yml)
[![Quality Gates](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/quality.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/quality.yml)
[![CodeQL](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/codeql-analysis.yml)
[![SonarCloud Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=krotname_JavaAlgorithmsShowcase&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=krotname_JavaAlgorithmsShowcase)
[![Coverage](https://codecov.io/gh/krotname/JavaAlgorithmsShowcase/branch/main/graph/badge.svg)](https://app.codecov.io/gh/krotname/JavaAlgorithmsShowcase)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/krotname/JavaAlgorithmsShowcase/badge)](https://securityscorecards.dev/viewer/?uri=github.com/krotname/JavaAlgorithmsShowcase)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13151/badge)](https://www.bestpractices.dev/projects/13151)
Expand All @@ -21,8 +22,8 @@ Curated Java reference implementations for deterministic algorithms and stateful

| Component | Engineering problem | What it demonstrates |
| --- | --- | --- |
| [Transaction validation](src/main/java/transactions) | Ordered, stateful operations; cascading invalidation; balance and overflow boundaries | Deterministic state transitions, immutable results, integration scenarios |
| [Algorithm utilities](src/main/java/algorithms) | Boundary conditions, complexity, stream and file I/O | Deterministic APIs, explicit complexity tradeoffs, edge cases, and property-based invariants |
| [Transaction validation](src/main/java/transactions) | Ordered, stateful operations; cascading invalidation; balance and overflow boundaries | Deterministic state transitions, immutable results, integration and property-based tests |
| [Algorithm utilities](src/main/java/algorithms) | Boundary conditions, complexity, stream and file I/O | Deterministic APIs, explicit complexity tradeoffs, and edge cases |
| [Parsing / validation](src/main/java/common/SafeParse.java) | Malformed numeric and text input | Explicit contracts, preserved exception causality, unit tests, and static analysis |

This is an engineering demonstration repository, not a commercial product. Implementations are selected to make code quality, test strategy, and the development process directly reviewable.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Curated Java reference implementations for deterministic algorithms and stateful
[![CI](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/maven.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/maven.yml)
[![Quality Gates](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/quality.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/quality.yml)
[![CodeQL](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/krotname/JavaAlgorithmsShowcase/actions/workflows/codeql-analysis.yml)
[![SonarCloud Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=krotname_JavaAlgorithmsShowcase&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=krotname_JavaAlgorithmsShowcase)
[![Coverage](https://codecov.io/gh/krotname/JavaAlgorithmsShowcase/branch/main/graph/badge.svg)](https://app.codecov.io/gh/krotname/JavaAlgorithmsShowcase)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/krotname/JavaAlgorithmsShowcase/badge)](https://securityscorecards.dev/viewer/?uri=github.com/krotname/JavaAlgorithmsShowcase)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13151/badge)](https://www.bestpractices.dev/projects/13151)
Expand All @@ -21,8 +22,8 @@ Curated Java reference implementations for deterministic algorithms and stateful

| Компонент | Инженерная задача | Что демонстрирует |
| --- | --- | --- |
| [Transaction validation](src/main/java/transactions) | Порядок и состояние операций, каскадная невалидность, баланс и переполнение | Детерминированные переходы состояния, неизменяемый результат, интеграционные сценарии |
| [Algorithm utilities](src/main/java/algorithms) | Граничные условия, сложность, поточный и файловый ввод-вывод | Детерминированные API, явные компромиссы по сложности, edge cases и property-based инварианты |
| [Transaction validation](src/main/java/transactions) | Порядок и состояние операций, каскадная невалидность, баланс и переполнение | Детерминированные переходы состояния, неизменяемый результат, интеграционные и property-based тесты |
| [Algorithm utilities](src/main/java/algorithms) | Граничные условия, сложность, поточный и файловый ввод-вывод | Детерминированные API, явные компромиссы по сложности и edge cases |
| [Parsing / validation](src/main/java/common/SafeParse.java) | Некорректный числовой и текстовый ввод | Явные контракты, сохранение причины исключения, unit-тесты и статический анализ |

Это демонстрационный инженерный репозиторий, а не коммерческий продукт. Реализации отобраны так, чтобы на ревью были видны качество кода, тестовая стратегия и воспроизводимый процесс разработки.
Expand Down
17 changes: 14 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@
<jacoco.version>0.8.15</jacoco.version>
<checkstyle.version>3.6.0</checkstyle.version>
<pmd.version>3.28.0</pmd.version>
<spotbugs.maven.version>4.9.8.5</spotbugs.maven.version>
<spotbugs.maven.version>4.10.3.0</spotbugs.maven.version>
<sonar.maven.version>5.7.0.6970</sonar.maven.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>6.1.1</junit.version>
<junit.version>6.1.2</junit.version>
<assertj.version>3.27.7</assertj.version>
<jqwik.version>1.10.1</jqwik.version>
<junit.platform.version>6.1.1</junit.platform.version>
<junit.platform.version>6.1.2</junit.platform.version>
<lombok.version>1.18.46</lombok.version>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.organization>krotname</sonar.organization>
<sonar.projectKey>krotname_JavaAlgorithmsShowcase</sonar.projectKey>
<sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
<coverage.line.minimum>0.70</coverage.line.minimum>
<coverage.branch.minimum>0.70</coverage.branch.minimum>
<coverage.instruction.minimum>0.70</coverage.instruction.minimum>
Expand Down Expand Up @@ -233,6 +238,12 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>${sonar.maven.version}</version>
</plugin>
</plugins>
</build>

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/interview/CallbackScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* Класс CallbackSchedulerImpl планирует исполнение Runnable в заданный момент времени.
* Задачи делегируются ScheduledExecutorService, чтобы не держать отдельный ручной worker-loop.
*/
public class CallbackScheduler implements AutoCloseable {
public final class CallbackScheduler implements AutoCloseable {

private final ScheduledExecutorService scheduler;

Expand Down
8 changes: 5 additions & 3 deletions src/main/java/kyu6/TribonacciSequence.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

public class TribonacciSequence {

private static final int SIGNATURE_SIZE = 3;

//6 https://www.codewars.com/kata/556deca17c58da83c00002db

public static double[] tribonacci(double[] s, int n) {
if (s == null || s.length != 3) {
if (s == null || s.length != SIGNATURE_SIZE) {
throw new IllegalArgumentException("signature must contain exactly three values");
}
if (n < 0) {
throw new IllegalArgumentException("sequence length must not be negative");
}
double[] result = new double[n];
int signatureLength = Math.min(s.length, n);
int signatureLength = Math.min(SIGNATURE_SIZE, n);
System.arraycopy(s, 0, result, 0, signatureLength);
for (int i = s.length; i < n; i++) {
for (int i = SIGNATURE_SIZE; i < n; i++) {
result[i] = result[i - 1] + result[i - 2] + result[i - 3];
}
return result;
Expand Down
17 changes: 9 additions & 8 deletions src/main/java/transactions/ValidationResult.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package transactions;

import lombok.Getter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Getter
final class ValidationResult {
private final List<TransactionStatus> transactions;

public ValidationResult(List<TransactionStatus> transactions) {
this.transactions = List.copyOf(transactions);
record ValidationResult(List<TransactionStatus> transactions) {
ValidationResult {
transactions = List.copyOf(transactions);
}

public static ValidationResult empty() {
return new ValidationResult(List.of());
}

public List<TransactionStatus> getTransactions() {
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
return Collections.unmodifiableList(new ArrayList<>(transactions));
}
}
4 changes: 2 additions & 2 deletions src/test/java/quality/PropertySuite.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
* to additional property-oriented classes when needed.
*/
@Suite
@SelectPackages({"kyu6", "kyu7"})
@IncludeClassNamePatterns(".*(toCamelCaseTest|SumOddNumbersPropertyTest|ValidatePinPropertyTest)")
@SelectPackages({"kyu6", "kyu7", "transactions"})
@IncludeClassNamePatterns(".*(toCamelCaseTest|SumOddNumbersPropertyTest|ValidatePinPropertyTest|InMemoryValidationServicePropertyTest)")
@IncludeTags("property")
public final class PropertySuite {
private PropertySuite() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package transactions;

import net.jqwik.api.Arbitraries;
import net.jqwik.api.Arbitrary;
import net.jqwik.api.Combinators;
import net.jqwik.api.ForAll;
import net.jqwik.api.Property;
import net.jqwik.api.Provide;
import net.jqwik.api.Tag;
import net.jqwik.api.constraints.LongRange;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Tag("property")
class InMemoryValidationServicePropertyTest {
private final ValidationService service = ValidationService.getInstance();

@Property(tries = 250)
void validationIsIndependentOfInputOrder(
@ForAll("transactionBatches") List<Transaction> transactions,
@ForAll @LongRange(min = 0, max = 1_000_000) long balance) {
List<Transaction> reversed = new ArrayList<>(transactions);
Collections.reverse(reversed);

List<TransactionStatus> directResult = service.validate(transactions, balance).getTransactions();
List<TransactionStatus> reversedResult = service.validate(reversed, balance).getTransactions();

assertEquals(directResult, reversedResult);
assertEquals(transactions.size(), directResult.size());
}

@Property(tries = 250)
void everyOrderHasOneConsistentStatus(
@ForAll("transactionBatches") List<Transaction> transactions,
@ForAll @LongRange(min = 0, max = 1_000_000) long balance) {
List<TransactionStatus> statuses = service.validate(transactions, balance).getTransactions();
Map<Long, Boolean> statusByOrder = new HashMap<>();

for (int index = 0; index < statuses.size(); index++) {
TransactionStatus current = statuses.get(index);
if (index > 0) {
assertTrue(statuses.get(index - 1).transaction().id() < current.transaction().id());
}
Boolean existing = statusByOrder.putIfAbsent(current.transaction().orderId(), current.status());
if (existing != null) {
assertEquals(existing, current.status());
}
}
}

@Property(tries = 150)
void nonOverflowingWinsAreAlwaysAccepted(
@ForAll("winOnlyBatches") List<Transaction> transactions,
@ForAll @LongRange(min = 0, max = 1_000_000) long balance) {
List<TransactionStatus> statuses = service.validate(transactions, balance).getTransactions();

assertEquals(transactions.size(), statuses.size());
assertTrue(statuses.stream().allMatch(TransactionStatus::status));
}

@Provide
Arbitrary<List<Transaction>> transactionBatches() {
Arbitrary<TransactionSeed> seed = Combinators.combine(
Arbitraries.longs().between(0, 10),
Arbitraries.longs().between(0, 10_000),
Arbitraries.of(TransactionType.values())
).as(TransactionSeed::new);

return seed.list().ofMaxSize(30)
.map(InMemoryValidationServicePropertyTest::toTransactions);
}

@Provide
Arbitrary<List<Transaction>> winOnlyBatches() {
return Arbitraries.longs().between(0, 10_000)
.list().ofMaxSize(30)
.map(amounts -> {
List<Transaction> transactions = new ArrayList<>(amounts.size());
for (int index = 0; index < amounts.size(); index++) {
transactions.add(new Transaction(index + 1L, index % 7L, amounts.get(index), TransactionType.WIN));
}
return transactions;
});
}

private static List<Transaction> toTransactions(List<TransactionSeed> seeds) {
List<Transaction> transactions = new ArrayList<>(seeds.size());
for (int index = 0; index < seeds.size(); index++) {
TransactionSeed seed = seeds.get(index);
transactions.add(new Transaction(index + 1L, seed.orderId(), seed.amount(), seed.type()));
}
return transactions;
}

private record TransactionSeed(long orderId, long amount, TransactionType type) {
}
}