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
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.fmazmz.casemanager.ticket.orchestration;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fmazmz.casemanager.ticket.model.TicketType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class TicketNumberGenerator {

private static final int NUMBER_PADDING = 7;

private final JdbcTemplate jdbcTemplate;

public String generate(TicketType type) {
String sequenceName = getSequenceName(type);
Long nextVal = jdbcTemplate.queryForObject(
"SELECT nextval('" + sequenceName + "')", Long.class);

if (nextVal == null) {
log.error("Failed to get next sequence value for type: {}", type);
throw new IllegalStateException("Sequence returned null for: " + sequenceName);
}

String number = formatNumber(type.getLabel(), nextVal);
log.info("Generated ticket number: {} for type: {}", number, type);
return number;
}

String getSequenceName(TicketType type) {
return "ticket_seq_" + type.name().toLowerCase();
}

String formatNumber(String prefix, Long sequence) {
return String.format("%s%0" + NUMBER_PADDING + "d", prefix, sequence);
}
}
8 changes: 7 additions & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ spring:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
show-sql: true
defer-datasource-initialization: true

sql:
init:
mode: always
platform: postgresql
4 changes: 3 additions & 1 deletion src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ spring:
hibernate:
ddl-auto: create-drop
show-sql: false
defer-datasource-initialization: true

sql:
init:
mode: never
mode: always
platform: h2

app:
storage:
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/data/ticket-number-sequence-schema-h2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE SEQUENCE IF NOT EXISTS ticket_seq_incident START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE IF NOT EXISTS ticket_seq_request START WITH 1 INCREMENT BY 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE SEQUENCE IF NOT EXISTS ticket_seq_incident START 1 INCREMENT 1;
CREATE SEQUENCE IF NOT EXISTS ticket_seq_request START 1 INCREMENT 1;
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest
@ActiveProfiles("test")
class CaseManagerApplicationTests {

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.fmazmz.casemanager.ticket.orchestration;

import org.fmazmz.casemanager.ticket.model.TicketType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class TicketNumberGeneratorTests {

@Mock
private JdbcTemplate jdbcTemplate;

private TicketNumberGenerator generator;

@BeforeEach
void setUp() {
generator = new TicketNumberGenerator(jdbcTemplate);
}

@Nested
@DisplayName("formatNumber")
class FormatNumberTests {

@Test
@DisplayName("formats number with correct prefix and padding")
void formatsWithPrefixAndPadding() {
String result = generator.formatNumber("INC", 1L);
assertEquals("INC0000001", result);
}

@Test
@DisplayName("pads to 7 digits for small numbers")
void padsSmallNumbers() {
String result = generator.formatNumber("REQ", 42L);
assertEquals("REQ0000042", result);
}

@Test
@DisplayName("handles large numbers without truncation")
void handlesLargeNumbers() {
String result = generator.formatNumber("INC", 9999999L);
assertEquals("INC9999999", result);
}

@Test
@DisplayName("handles numbers exceeding 7 digits")
void handlesOverflow() {
String result = generator.formatNumber("INC", 12345678L);
assertEquals("INC12345678", result);
}

@ParameterizedTest
@CsvSource({
"INC, 1, INC0000001",
"INC, 999, INC0000999",
"REQ, 1, REQ0000001",
"REQ, 123456, REQ0123456"
})
@DisplayName("formats various prefix and number combinations correctly")
void formatsVariousCombinations(String prefix, Long sequence, String expected) {
String result = generator.formatNumber(prefix, sequence);
assertEquals(expected, result);
}
}

@Nested
@DisplayName("getSequenceName")
class GetSequenceNameTests {

@Test
@DisplayName("returns correct sequence name for INCIDENT")
void returnsIncidentSequenceName() {
String result = generator.getSequenceName(TicketType.INCIDENT);
assertEquals("ticket_seq_incident", result);
}

@Test
@DisplayName("returns correct sequence name for REQUEST")
void returnsRequestSequenceName() {
String result = generator.getSequenceName(TicketType.REQUEST);
assertEquals("ticket_seq_request", result);
}
}

@Nested
@DisplayName("generate")
class GenerateTests {

@Test
@DisplayName("generates ticket number for INCIDENT type")
void generatesIncidentNumber() {
when(jdbcTemplate.queryForObject(
eq("SELECT nextval('ticket_seq_incident')"),
eq(Long.class)))
.thenReturn(1L);

String result = generator.generate(TicketType.INCIDENT);

assertEquals("INC0000001", result);
}

@Test
@DisplayName("generates ticket number for REQUEST type")
void generatesRequestNumber() {
when(jdbcTemplate.queryForObject(
eq("SELECT nextval('ticket_seq_request')"),
eq(Long.class)))
.thenReturn(5L);

String result = generator.generate(TicketType.REQUEST);

assertEquals("REQ0000005", result);
}

@Test
@DisplayName("throws exception when sequence returns null")
void throwsOnNullSequence() {
when(jdbcTemplate.queryForObject(anyString(), eq(Long.class)))
.thenReturn(null);

IllegalStateException ex = assertThrows(
IllegalStateException.class,
() -> generator.generate(TicketType.INCIDENT)
);

assertTrue(ex.getMessage().contains("ticket_seq_incident"));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.fmazmz.casemanager.unit;
package org.fmazmz.casemanager.ticket.typehandlers;

import org.fmazmz.casemanager.ticket.model.Priority;
import org.fmazmz.casemanager.ticket.model.Ticket;
Expand Down