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
Expand Up @@ -70,7 +70,7 @@ and not exists (
} catch (IllegalArgumentException ignored) {
return List.of();
}
List<UUID> workerIds = entityManager.createQuery(
List<Object[]> candidateRows = entityManager.createQuery(
"""
select worker.workerId, worker.displayName
from WorkerJpaEntity worker
Expand All @@ -86,12 +86,45 @@ and not exists (
Object[].class
)
.setParameter("companyId", companyId)
.getResultList()
.getResultList();
List<UUID> workerIds = candidateRows
.stream()
.filter(row -> lookupKey.equals(displayNameNormalizer.normalize((String) row[1])))
.limit(2)
.map(row -> (UUID) row[0])
.toList();
if (workerIds.isEmpty() && lookupKey.codePointCount(0, lookupKey.length()) >= 3) {
workerIds = candidateRows
.stream()
.filter(row -> displayNameNormalizer.normalize((String) row[1])
.startsWith(lookupKey))
.limit(2)
.map(row -> (UUID) row[0])
.toList();
}
if (workerIds.isEmpty()) {
List<Object[]> sentencePrefixCandidates = candidateRows.stream()
.filter(row -> {
String candidateKey = displayNameNormalizer.normalize((String) row[1]);
return candidateKey.codePointCount(0, candidateKey.length()) >= 3
&& lookupKey.startsWith(candidateKey);
})
.toList();
int longestCandidateLength = sentencePrefixCandidates.stream()
.map(row -> displayNameNormalizer.normalize((String) row[1]))
.mapToInt(value -> value.codePointCount(0, value.length()))
.max()
.orElse(0);
workerIds = sentencePrefixCandidates.stream()
.filter(row -> {
String candidateKey = displayNameNormalizer.normalize((String) row[1]);
return candidateKey.codePointCount(0, candidateKey.length())
== longestCandidateLength;
})
.limit(2)
.map(row -> (UUID) row[0])
.toList();
}
if (workerIds.isEmpty()) {
return List.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,84 @@ void multipleNormalizedCandidatesAreReportedAsAmbiguous() {
);
}

@Test
void resolvesUniqueNormalizedPrefixInsideTheCurrentCompany() {
insertCompany(COMPANY_A, "사업장 A");
insertWorker(WORKER_A, COMPANY_A, "응웬반A", "2026-09-30");

AiSlotResolution result = resolutionTransaction.resolve(
COMPANY_A,
"0.3.1",
requirement("응웬반")
);

assertThat(result.worker().workerRef()).isEqualTo(WORKER_A);
}

@Test
void resolvesWorkerWhenAgentReturnsSentenceStartingWithTheDisplayName() {
insertCompany(COMPANY_A, "사업장 A");
insertWorker(WORKER_A, COMPANY_A, "응웬반A", "2026-09-30");

AiSlotResolution result = resolutionTransaction.resolve(
COMPANY_A,
"0.3.1",
requirement("응웬반A가 3년 만료 예정이야.")
);

assertThat(result.worker().workerRef()).isEqualTo(WORKER_A);
}

@Test
void longestDisplayNameWinsWhenAgentReturnsANamePrefixedSentence() {
insertCompany(COMPANY_A, "사업장 A");
insertWorker(WORKER_A, COMPANY_A, "응웬반", "2026-09-30");
insertWorker(WORKER_A_DUPLICATE, COMPANY_A, "응웬반A", "2027-09-30");

AiSlotResolution result = resolutionTransaction.resolve(
COMPANY_A,
"0.3.1",
requirement("응웬반A가 3년 만료 예정이야.")
);

assertThat(result.worker().workerRef()).isEqualTo(WORKER_A_DUPLICATE);
}

@Test
void multipleNormalizedPrefixCandidatesAreReportedAsAmbiguous() {
insertCompany(COMPANY_A, "사업장 A");
insertWorker(WORKER_A, COMPANY_A, "응웬반A", "2026-09-30");
insertWorker(WORKER_A_DUPLICATE, COMPANY_A, "응웬반B", "2027-09-30");

assertThatThrownBy(() -> resolutionTransaction.resolve(
COMPANY_A,
"0.3.1",
requirement("응웬반")
))
.isInstanceOfSatisfying(AiContextResolutionException.class, exception ->
assertThat(exception.failureCode()).isEqualTo(
AiContextResolutionFailureCode.TARGET_AMBIGUOUS
)
);
}

@Test
void shortPrefixIsNotUsedForWorkerResolution() {
insertCompany(COMPANY_A, "사업장 A");
insertWorker(WORKER_A, COMPANY_A, "응웬반A", "2026-09-30");

assertThatThrownBy(() -> resolutionTransaction.resolve(
COMPANY_A,
"0.3.1",
requirement("응")
))
.isInstanceOfSatisfying(AiContextResolutionException.class, exception ->
assertThat(exception.failureCode()).isEqualTo(
AiContextResolutionFailureCode.TARGET_NOT_FOUND
)
);
}

@Test
void separatorOnlyTargetIsReportedAsNotFound() {
insertCompany(COMPANY_A, "사업장 A");
Expand Down
Loading