Compare commits

...

22 commits

Author SHA1 Message Date
b25f76dde8
Merge pull request 'test: dice service' (!237) from refactor-blackjack into main
All checks were successful
Release / Release (push) Successful in 1m1s
Release / Build Frontend Image (push) Successful in 30s
Release / Build Backend Image (push) Successful in 1m1s
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Has been skipped
CI / Backend Tests (pull_request) Has been skipped
CI / oxlint (pull_request) Has been skipped
CI / Checkstyle Main (pull_request) Has been skipped
CI / prettier (pull_request) Has been skipped
CI / Docker frontend validation (pull_request) Has been skipped
CI / test-build (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
Claude PR Review / claude-code (pull_request) Successful in 1m11s
Reviewed-on: #237
Reviewed-by: Jan K9f <jan@kjan.email>
2025-06-02 12:16:43 +00:00
aa7cf00ebb
Merge pull request 'chore: Add stale pipeline' (!253) from stale into main
Reviewed-on: #253
Reviewed-by: Constantin Simonis <constantin@simonis.lol>
2025-06-02 12:15:30 +00:00
bd1d8f8339 chore: Add stale pipeline
All checks were successful
CI / Get Changed Files (pull_request) Successful in 10s
CI / oxlint (pull_request) Successful in 28s
CI / eslint (pull_request) Successful in 39s
CI / prettier (pull_request) Successful in 41s
Claude PR Review / claude-code (pull_request) Successful in 1m5s
CI / Docker frontend validation (pull_request) Successful in 17s
CI / test-build (pull_request) Successful in 1m17s
CI / Docker backend validation (pull_request) Successful in 16s
CI / Checkstyle Main (pull_request) Successful in 1m19s
CI / Backend Tests (pull_request) Successful in 2m15s
2025-06-02 14:13:37 +02:00
fa67dd5ebf
Merge pull request 'fix: Update claude to not decline pr's' (!244) from claude/no-reject into main
Reviewed-on: #244
Reviewed-by: Claude <claude@kjan.email>
2025-06-01 11:54:56 +00:00
b09c9c3b4f
Merge branch 'main' into claude/no-reject
All checks were successful
CI / Get Changed Files (pull_request) Successful in 11s
CI / Docker frontend validation (pull_request) Successful in 32s
CI / eslint (pull_request) Successful in 41s
CI / oxlint (pull_request) Successful in 44s
CI / prettier (pull_request) Successful in 46s
CI / Docker backend validation (pull_request) Successful in 21s
Claude PR Review / claude-code (pull_request) Successful in 1m14s
CI / test-build (pull_request) Successful in 1m13s
CI / Checkstyle Main (pull_request) Successful in 1m22s
CI / Backend Tests (pull_request) Successful in 2m7s
2025-06-01 11:51:58 +00:00
4960b5966f
Merge pull request 'fix: Remove -m' (!250) from claude/no-m into main
Reviewed-on: #250
2025-06-01 11:45:11 +00:00
ffea4c0ec3 fix: Remove -m
All checks were successful
CI / Get Changed Files (pull_request) Successful in 12s
CI / eslint (pull_request) Successful in 24s
CI / oxlint (pull_request) Successful in 27s
CI / Docker frontend validation (pull_request) Successful in 29s
CI / prettier (pull_request) Successful in 42s
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m2s
CI / Docker backend validation (pull_request) Successful in 19s
CI / test-build (pull_request) Successful in 1m12s
CI / Checkstyle Main (pull_request) Successful in 1m20s
CI / Backend Tests (pull_request) Successful in 2m1s
2025-06-01 13:43:36 +02:00
ed44c8b7fd
Merge pull request 'fix: Force claude to use tea cli' (!249) from claude/force into main
Reviewed-on: #249
Reviewed-by: Claude <claude@kjan.email>
2025-06-01 11:40:04 +00:00
262c814df0 fix: Force claude to use tea cli
All checks were successful
CI / Get Changed Files (pull_request) Successful in 16s
CI / Docker frontend validation (pull_request) Successful in 17s
CI / Docker backend validation (pull_request) Successful in 16s
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m25s
CI / Checkstyle Main (pull_request) Successful in 52s
CI / Backend Tests (pull_request) Successful in 1m58s
CI / oxlint (pull_request) Successful in 20s
CI / eslint (pull_request) Successful in 32s
CI / prettier (pull_request) Successful in 23s
CI / test-build (pull_request) Successful in 32s
2025-06-01 13:39:39 +02:00
1de660d0e2
Merge pull request 'fix: Fix claude to always use tea command to review' (!248) from claude/use-tea-cli into main
Reviewed-on: #248
Reviewed-by: Claude <claude@kjan.email>
2025-06-01 10:30:51 +00:00
5d5c27827b fix: Fix claude to always use tea command to review
All checks were successful
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Successful in 30s
CI / oxlint (pull_request) Successful in 30s
CI / Docker frontend validation (pull_request) Successful in 32s
CI / prettier (pull_request) Successful in 41s
CI / Docker backend validation (pull_request) Successful in 16s
CI / test-build (pull_request) Successful in 1m6s
CI / Checkstyle Main (pull_request) Successful in 1m17s
CI / Backend Tests (pull_request) Successful in 1m54s
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m18s
2025-06-01 12:24:42 +02:00
2b096695a3
Merge pull request 'fix: Fix claude pipeline' (!247) from claude/fix into main
Reviewed-on: #247
2025-06-01 10:20:54 +00:00
84a4ede026 fix: Fix claude pipeline
All checks were successful
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Successful in 31s
CI / Docker frontend validation (pull_request) Successful in 29s
CI / oxlint (pull_request) Successful in 36s
CI / prettier (pull_request) Successful in 40s
CI / Docker backend validation (pull_request) Successful in 19s
CI / test-build (pull_request) Successful in 1m9s
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m27s
CI / Checkstyle Main (pull_request) Successful in 1m19s
CI / Backend Tests (pull_request) Successful in 1m56s
2025-06-01 12:16:44 +02:00
f6b8400c0b
Merge branch 'main' into claude/no-reject
All checks were successful
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Successful in 34s
CI / Docker frontend validation (pull_request) Successful in 30s
CI / oxlint (pull_request) Successful in 42s
CI / prettier (pull_request) Successful in 45s
Claude PR Review / claude-code (pull_request) Successful in 57s
CI / Docker backend validation (pull_request) Successful in 20s
CI / test-build (pull_request) Successful in 1m11s
CI / Checkstyle Main (pull_request) Successful in 1m18s
CI / Backend Tests (pull_request) Successful in 1m58s
2025-06-01 10:14:21 +00:00
f991e1d6ed
Merge pull request 'chore: Claude can respond in pr's' (!246) from claude/pr-response into main
Reviewed-on: #246
Reviewed-by: Claude <claude@kjan.email>
2025-06-01 10:14:13 +00:00
c719e43ba0 chore: Claude can respond in pr's
All checks were successful
CI / Get Changed Files (pull_request) Successful in 17s
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m27s
CI / eslint (pull_request) Successful in 56s
CI / oxlint (pull_request) Successful in 38s
CI / prettier (pull_request) Successful in 50s
CI / test-build (pull_request) Successful in 1m40s
CI / Docker frontend validation (pull_request) Successful in 20s
CI / Docker backend validation (pull_request) Successful in 17s
CI / Checkstyle Main (pull_request) Successful in 1m26s
CI / Backend Tests (pull_request) Successful in 2m58s
2025-06-01 12:08:02 +02:00
5ca152007e Merge branch 'main' into claude/pr-response 2025-06-01 12:06:50 +02:00
189fb85918 fix: Update pipeline name
Some checks failed
CI / Checkstyle Main (pull_request) Successful in 53s
CI / Get Changed Files (pull_request) Successful in 8s
CI / eslint (pull_request) Successful in 27s
CI / oxlint (pull_request) Successful in 28s
CI / prettier (pull_request) Successful in 35s
CI / test-build (pull_request) Successful in 51s
CI / Backend Tests (pull_request) Successful in 2m7s
Claude PR Review / claude-code (pull_request) Successful in 52s
CI / Docker backend validation (pull_request) Successful in 11s
CI / Docker frontend validation (pull_request) Failing after 14s
2025-06-01 10:44:47 +02:00
acd098225c fix: Update claude to not decline pr's 2025-06-01 10:44:44 +02:00
Phan Huy Tran
3e1c15e023 test: adjust commments
All checks were successful
CI / Get Changed Files (pull_request) Successful in 11s
CI / eslint (pull_request) Has been skipped
CI / oxlint (pull_request) Has been skipped
CI / prettier (pull_request) Has been skipped
CI / test-build (pull_request) Has been skipped
Setup Gitea Tea CLI / setup-and-login (pull_request) Successful in 1m38s
CI / Docker frontend validation (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Successful in 18s
CI / Checkstyle Main (pull_request) Successful in 1m14s
CI / Backend Tests (pull_request) Successful in 2m16s
2025-05-28 11:04:57 +00:00
Phan Huy Tran
78b8f4696c test: refactor 2025-05-28 11:04:57 +00:00
Phan Huy Tran
b7a8627bcf test: dice service 2025-05-28 11:04:57 +00:00
7 changed files with 397 additions and 7 deletions

View file

@ -0,0 +1,124 @@
name: Claude Gitea PR Interaction via Comment
on:
issue_comment:
types: [created]
jobs:
claude-interact-on-comment:
runs-on: ubuntu-latest
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '@Claude')
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for git diff against main/master
- name: Set Tea Version
id: tea_version
run: echo "version=0.9.2" >> $GITHUB_OUTPUT # Check for the latest stable version
- name: Download Tea CLI
run: |
TEA_VERSION=$(echo "${{ steps.tea_version.outputs.version }}")
wget "https://gitea.com/gitea/tea/releases/download/v${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" -O tea
chmod +x tea
sudo mv tea /usr/local/bin/tea
- name: Verify Tea Installation
run: tea --version
- name: Add Gitea Login
env:
GITEA_URL: ${{ secrets._GITEA_URL }}
GITEA_TOKEN: ${{ secrets._GITEA_TOKEN }}
run: |
if [ -z "$GITEA_URL" ]; then
echo "Error: GITEA_URL secret is not set."
exit 1
fi
if [ -z "$GITEA_TOKEN" ]; then
echo "Error: GITEA_TOKEN secret is not set."
exit 1
fi
INSECURE_FLAG=""
if [[ "${GITEA_URL}" == http://* ]]; then
INSECURE_FLAG="--insecure"
fi
tea login add --name mygitea --url "$GITEA_URL" --token "$GITEA_TOKEN" $INSECURE_FLAG
- name: Install bun
uses: oven-sh/setup-bun@v2
- name: Install claude-code
run: bun i -g @anthropic-ai/claude-code
- name: Claude Process PR Comment
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PR_NUMBER: ${{ github.event.issue.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
GITEA_URL: ${{ secrets._GITEA_URL }}
run: |
claude --allowedTools "Bash(tea:*)" --allowedTools "Bash(git:*)" --allowedTools "Read" --allowedTools "Grep" --allowedTools "WebFetch" --allowedTools "Glob" --allowedTools "LS" -p "You are an AI assistant integrated with Gitea (at ${GITEA_URL}) via the 'tea' CLI.
You have been invoked because user '${COMMENT_AUTHOR}' left the following comment on Pull Request #${PR_NUMBER}:
---
${COMMENT_BODY}
---
Your primary task is to:
1. Carefully understand the user's request within their comment.
2. Use the 'tea' CLI to perform the requested action(s) on Pull Request #${PR_NUMBER}.
3. If the request is to review the PR, fetch the diff against the PR's base branch (e.g., 'git fetch origin main && git diff origin/main...HEAD' or similar; adapt branch name if necessary, or use 'tea pr diff ${PR_NUMBER}') and provide constructive feedback.
4. For other actions, translate the user's intent into the appropriate 'tea' command.
**How to Post Reviews and Other Feedback:**
- When you provide a review, post it as a comment using:
\`tea pr comment ${PR_NUMBER} \"Claude's Review:\n[Your detailed review, mentioning files and line numbers.]\"\`
- For other informational responses or clarifications, also use \`tea pr comment ...\`.
**Critical: Handling Approval, Rejection, or Merge Requests:**
Pull Request approval, rejection, and merging are critical actions and should not be used to 'cheat' the review process. You cannot verify Gitea user permissions.
- If a user comments asking you to directly approve (e.g., '@claude approve this'), merge, or reject a PR:
1. **Do NOT blindly execute these commands.**
2. **Approval/Merge:**
- State in a comment (using \`tea pr comment ...\`) that direct approval/merge requests via you are typically for convenience after a proper human review process has been implicitly completed or if the requester is a designated maintainer explicitly overriding.
- If the PR has not been reviewed by you yet, and the comment implies a review is also needed, perform the review FIRST and post it.
- You should only consider proceeding with a \`tea pr approve ${PR_NUMBER}\` or \`tea pr merge ${PR_NUMBER}\` command if:
a. The comment explicitly states that all necessary human reviews are complete and this is just a formal step by a trusted user.
b. OR, your own comprehensive review found no critical issues and the request seems appropriate in context.
- If in doubt, default to posting your review (if applicable) and stating that a maintainer should perform the final approval/merge. Your goal is to assist, not to bypass established review procedures.
3. **Rejection/Requesting Changes:**
- If asked to reject or request changes, you should typically base this on your own review of the PR's changes.
- First, perform a review if you haven't already.
- Then, you can use \`tea pr reject ${PR_NUMBER} \"Claude's Review Summary: [summary of reasons for rejection/changes based on your review]\"\`. Ensure your detailed review is also available as a comment.
Examples of interpreting comments and generating appropriate \`tea\` commands (keeping the above critical guidelines in mind):
- User: '@claude LGTM, approve this' -> You: First, consider if a review is implied or done. If so, and you agree, you might generate \`tea pr approve ${PR_NUMBER}\`. If not, you might generate \`tea pr comment ${PR_NUMBER} \"Claude: I can approve this if the standard review process is complete. Have maintainers reviewed this?\"\` or perform your own review and then comment.
- User: '@claude please review this PR' -> You: Get diffs, review, then generate \`tea pr comment ${PR_NUMBER} \"Claude's Review: ...\"\`.
- User: '@claude close this PR' -> You: Generate \`tea pr close ${PR_NUMBER}\` and optionally \`tea pr comment ${PR_NUMBER} \"Claude: PR #${PR_NUMBER} has been closed as requested.\"\`.
- User: '@claude add label enhancement' -> You: Generate \`tea pr label ${PR_NUMBER} --add enhancement\` and \`tea pr comment ${PR_NUMBER} \"Claude: Label 'enhancement' added to PR #${PR_NUMBER}.\"\`
- User: '@claude what are the labels on this PR?' -> You: Generate \`tea pr label ${PR_NUMBER} --list\` (this command outputs to stdout, which is fine for your internal use). Then, to inform the user, you generate: \`tea pr comment ${PR_NUMBER} \"Claude: The current labels are: [output from tea pr label --list].\"\` (You'll need to capture the output of the first command to formulate the second if the tool allows such chaining, otherwise, focus on commands that directly achieve the user's goal or report information). *Self-correction: The Bash tool can capture output. So, if you need to run a \`tea\` command to get information for yourself, do so, then use that information to formulate your \`tea pr comment ...\` to the user.*
**IMPORTANT GUIDELINES FOR YOUR OPERATION AND RESPONSE GENERATION:**
- **Your SOLE METHOD of communicating back to the user on Gitea is by generating a \`tea pr comment ${PR_NUMBER} \"...\"\` command.** This is non-negotiable. Do not output plain text messages intended for the user. Your response *is* the command.
- **Use the 'tea' CLI for ALL Gitea interactions.** This includes fetching PR details, diffs, labels, status, and posting comments, reviews, approvals, etc.
- **For PR reviews, ALWAYS analyze the diff.** Use \`tea pr diff ${PR_NUMBER}\` or git commands to get the diff. Make sure to mention specific files and line numbers in your review comment.
- **Be precise with 'tea' commands.** If a user's request is ambiguous, DO NOT GUESS. Instead, generate a \`tea pr comment ${PR_NUMBER} \"Claude Asks: [Your clarifying question]\"\` command to ask for more details.
- **Execute only necessary 'tea' command(s).** If a user asks for a review, your primary output should be the \`tea pr comment ...\` command containing the review. If they ask to add a label, your output should be \`tea pr label ...\` and then a confirmation \`tea pr comment ...\`.
- **Ensure reviews are professional, constructive, and helpful.**
- **If you need to perform an action AND then report on it, generate both \`tea\` commands sequentially.** For example, to add a label and confirm:
\`tea pr label ${PR_NUMBER} --add bug\`
\`tea pr comment ${PR_NUMBER} "Claude: I've added the 'bug' label."\`
The GitHub Actions workflow will execute these commands one after another.
- **If a user's request cannot be fulfilled using the 'tea' CLI or the allowed tools, explain this limitation by generating a \`tea pr comment ...\` command.** For example: \`tea pr comment ${PR_NUMBER} "Claude: I cannot perform that action as it's outside my current capabilities or allowed tools."\`
- **Think step-by-step.** 1. Understand request. 2. Identify necessary `tea` command(s). 3. If it's a review, get the diff. 4. Formulate the `tea` command(s) as your direct output.
**Final Check before outputting:**
"Is my entire response that's intended for the Gitea user wrapped in a \`tea pr comment ${PR_NUMBER} '...' \` command (or another appropriate \`tea\` command if it's an action like \`tea pr label ...\`)? If not, I must fix it."
You are now ready to process the comment. Remember, your output will be executed in a shell. Generate only the \`tea\` command(s) needed.
"

View file

@ -1,11 +1,11 @@
name: Setup Gitea Tea CLI
name: Claude PR Review
on:
pull_request:
types: [opened, synchronize] # Runs on new PRs and updates
jobs:
setup-and-login:
claude-code:
runs-on: ubuntu-latest
steps:
- name: Checkout code
@ -69,7 +69,7 @@ jobs:
Once you are done with your review, post your feedback as a reject or review on the pull request using the following exact format:
tea \"<reject or approve>\" ${PR_NUMBER} \"<your review message here>\"
tea \"<comment or approve>\" ${PR_NUMBER} \"<your review message here>\"
Make sure the comment is clear, professional, and helpful. Only run the tea comment command once you're finished reviewing all changes. AND MOST IMPORTANDLY ONLY REVIEW THE DIFF FROM THE CURRENT STATE TO THE MAIN BRANCH TO GET THAT USE GIT DIFF
You may also use the tea cli to find out various things about the pull request

View file

@ -0,0 +1,14 @@
name: "Close stale issues and PRs"
on:
schedule:
- cron: "30 1 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-pr-message: "Will be closed in x days bc yo mom is a bitch. im not telling you when it will be closed fuckface"
days-before-pr-stale: 2
days-before-pr-close: 3

View file

@ -4,8 +4,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import de.szut.casino.user.UserEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

View file

@ -5,12 +5,14 @@ import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
public class DiceDto extends BetDto {
private boolean rollOver;

View file

@ -11,10 +11,11 @@ import java.util.Random;
@Service
public class DiceService {
private static final int MAX_DICE_VALUE = 100;
private final Random random = new Random();
private final Random random;
private final BalanceService balanceService;
public DiceService(BalanceService balanceService) {
public DiceService(Random random, BalanceService balanceService) {
this.random = random;
this.balanceService = balanceService;
}

View file

@ -0,0 +1,251 @@
package de.szut.casino.dice;
import de.szut.casino.shared.service.BalanceService;
import de.szut.casino.user.UserEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class DiceServiceTest {
@Mock
private BalanceService balanceService;
@Mock
private Random random;
@InjectMocks
private DiceService diceService;
private UserEntity user;
private DiceDto diceDto;
@BeforeEach
void setUp() {
user = new UserEntity();
user.setId(1L);
user.setBalance(BigDecimal.valueOf(1000));
diceDto = new DiceDto();
diceDto.setBetAmount(BigDecimal.valueOf(10));
diceDto.setTargetValue(BigDecimal.valueOf(50));
diceDto.setRollOver(true);
}
@Test
void play_rollOver_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(50));
when(random.nextInt(anyInt())).thenReturn(55);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(56), result.getRolledValue());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollOver_lose() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(50));
when(random.nextInt(anyInt())).thenReturn(49);
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(50), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollUnder_win() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(50));
when(random.nextInt(anyInt())).thenReturn(48);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(49), result.getRolledValue());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollUnder_lose() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(50));
when(random.nextInt(anyInt())).thenReturn(50);
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(51), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollOver_targetValueOne_rolledOne_lose() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(1));
when(random.nextInt(anyInt())).thenReturn(0);
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollOver_targetValueOne_rolledTwo_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(1));
when(random.nextInt(anyInt())).thenReturn(1);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(2), result.getRolledValue());
// Win chance for target 1 (roll over) is 99. Multiplier = (100-1)/99 = 1
assertEquals(diceDto.getBetAmount().stripTrailingZeros(), result.getPayout().stripTrailingZeros());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollUnder_targetValueOne_alwaysLose_winChanceZero() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(1));
when(random.nextInt(anyInt())).thenReturn(0);
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(1), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollOver_targetValueNinetyNine_rolledHundred_win() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(99));
when(random.nextInt(anyInt())).thenReturn(99);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
// Win chance for target 99 (roll over) is 1. Multiplier = (100-1)/1 = 99
assertEquals(diceDto.getBetAmount().multiply(BigDecimal.valueOf(99)).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollUnder_targetValueNinetyNine_rolledNinetyEight_win() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(99));
when(random.nextInt(anyInt())).thenReturn(97);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(98), result.getRolledValue());
// Win chance for target 99 (roll under) is 98. Multiplier = (100-1)/98 = 99/98
assertEquals(diceDto.getBetAmount().multiply(BigDecimal.valueOf(99).divide(BigDecimal.valueOf(98), 4, RoundingMode.HALF_UP)), result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollOver_targetValueOneHundred_alwaysLose_winChanceZero() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(100));
when(random.nextInt(anyInt())).thenReturn(99);
DiceResult result = diceService.play(user, diceDto);
assertFalse(result.isWin());
assertEquals(BigDecimal.valueOf(100), result.getRolledValue());
assertEquals(BigDecimal.ZERO, result.getPayout());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, never()).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_rollUnder_targetValueOneHundred_rolledNinetyNine_win() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(100));
when(random.nextInt(anyInt())).thenReturn(98);
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(99), result.getRolledValue());
// Win chance for target 100 (roll under) is 99. Multiplier = (100-1)/99 = 1
assertEquals(diceDto.getBetAmount().stripTrailingZeros(), result.getPayout().stripTrailingZeros());
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
verify(balanceService, times(1)).addFunds(eq(user), any(BigDecimal.class));
}
@Test
void play_payoutCalculationCorrect() {
diceDto.setRollOver(true);
diceDto.setTargetValue(BigDecimal.valueOf(75));
when(random.nextInt(anyInt())).thenReturn(75);
// Multiplier for win chance 25: (100-1)/25 = 99/25 = 3.96
// Payout: 10 * 3.96 = 39.6
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(39.6).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
}
@Test
void play_payoutCalculationCorrect_rollUnder() {
diceDto.setRollOver(false);
diceDto.setTargetValue(BigDecimal.valueOf(25));
when(random.nextInt(anyInt())).thenReturn(0);
// Multiplier for win chance 24: (100-1)/24 = 99/24 = 4.125
// Payout: 10 * 4.125 = 41.25
DiceResult result = diceService.play(user, diceDto);
assertTrue(result.isWin());
assertEquals(BigDecimal.valueOf(41.25).stripTrailingZeros(), result.getPayout().stripTrailingZeros());
}
@Test
void play_betAmountSubtracted() {
when(random.nextInt(anyInt())).thenReturn(50);
diceService.play(user, diceDto);
verify(balanceService, times(1)).subtractFunds(user, diceDto.getBetAmount());
}
}