Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -47,11 +47,12 @@ class OrganizationStatsServiceTest : AbstractSpringTest() {
@Test
fun `getKeyCount counts unique keys across all projects in organization`() {
// Organization total:
// First project: 6 unique keys
// Second project: 2 unique keys
// Total: 8 unique keys
// First project (branching enabled): 6 unique keys
// Second project (branching enabled): 2 unique keys
// Third project (branching disabled): nb-key1 + nb-key3 = 2 keys (nb-key2 on orphan branch excluded)
// Total: 10 unique keys
val orgKeyCount = organizationStatsService.getKeyCount(testData.organization.id)
assertThat(orgKeyCount).isEqualTo(8)
assertThat(orgKeyCount).isEqualTo(10)
}

@Test
Expand All @@ -70,8 +71,36 @@ class OrganizationStatsServiceTest : AbstractSpringTest() {
// - key5 (main): EN, DE = 2
// Second project total: 3
//
// Organization total: 8
// Third project translations (branching disabled):
// - nb-key1: EN = 1
// - nb-key2 on orphan branch: excluded (branching disabled)
// Third project total: 1
//
// Organization total: 9
val translationCount = organizationStatsService.getTranslationCount(testData.organization.id)
assertThat(translationCount).isEqualTo(9)
}

@Test
fun `getKeyCount excludes branch keys when project has branching disabled`() {
// The no-branching project has useBranching=false with:
// - nb-key1 (no branch) = counted
// - nb-key2 (orphan feature branch) = NOT counted (branching disabled)
// - nb-key3 (no branch) = counted
// This is verified via the org-wide count which includes all three projects:
// First project: 6 + Second project: 2 + Third project: 2 = 10
val orgKeyCount = organizationStatsService.getKeyCount(testData.organization.id)
assertThat(orgKeyCount).isEqualTo(10)
}

@Test
fun `getTranslationCount excludes branch translations when project has branching disabled`() {
// The no-branching project has useBranching=false with:
// - nb-key1 EN translation = counted
// - nb-key2 EN translation on orphan branch = NOT counted (branching disabled)
// This is verified via the org-wide count:
// First project: 5 + Second project: 3 + Third project: 1 = 9
val translationCount = organizationStatsService.getTranslationCount(testData.organization.id)
assertThat(translationCount).isEqualTo(8)
assertThat(translationCount).isEqualTo(9)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class OrganizationStatsTestData : BaseTestData("org-stats", "Stats Project") {
lateinit var secondProjectFeatureBranch: Branch
lateinit var germanLanguage: Language
lateinit var secondProjectGermanLanguage: Language
lateinit var noBranchingProject: Project

init {
root.apply {
Expand All @@ -28,6 +29,7 @@ class OrganizationStatsTestData : BaseTestData("org-stats", "Stats Project") {
}

addSecondProject()
addNoBranchingProject()
}
}

Expand Down Expand Up @@ -196,4 +198,58 @@ class OrganizationStatsTestData : BaseTestData("org-stats", "Stats Project") {
}
}.self
}

/**
* Third project with useBranching=false but containing orphan branch keys.
* These branch keys should NOT be counted in org stats.
*/
private fun TestDataBuilder.addNoBranchingProject() {
noBranchingProject =
addProject {
name = "No Branching Project"
organizationOwner = this@OrganizationStatsTestData.organization
useBranching = false
}.build {
addLanguage {
name = "English"
tag = "en"
originalName = "English"
this@build.self.baseLanguage = this
}

val noBranchingMainBranch =
addBranch {
name = "main"
project = self
isDefault = true
}.build { self }.self

val noBranchingFeatureBranch =
addBranch {
name = "orphan-feature"
project = self
originBranch = noBranchingMainBranch
}.build { self }.self

// Key on main branch (branch_id is null for default) — should be counted
addKey {
name = "nb-key1"
}.build {
addTranslation("en", "NB Key 1 English")
}

// Key on orphan feature branch — should NOT be counted (branching disabled)
addKey {
name = "nb-key2"
branch = noBranchingFeatureBranch
}.build {
addTranslation("en", "NB Key 2 English on orphan branch")
}

// Key on main branch without translation — should be counted as key but not translation
addKey {
name = "nb-key3"
}
}.self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,17 @@ class OrganizationStatsService(
select distinct k.project_id, k.name, k.namespace_id, t.language_id
from translation t
join key k on k.id = t.key_id
where k.project_id in (
select p.id from project p
where p.organization_owner_id = :organizationId
and p.deleted_at is null
)
and k.deleted_at is null
and exists (
select 1 from language l
where l.id = t.language_id
and l.deleted_at is null
)
and t.text is not null
and t.text <> ''
join project p on p.id = k.project_id and p.deleted_at is null
where p.organization_owner_id = :organizationId
and k.deleted_at is null
and (p.use_branching = true or k.branch_id is null)
and exists (
select 1 from language l
where l.id = t.language_id
and l.deleted_at is null
)
and t.text is not null
and t.text <> ''
) sub
""".trimIndent(),
).setParameter("organizationId", organizationId)
Expand All @@ -79,6 +77,7 @@ class OrganizationStatsService(
join project p on p.id = k.project_id and p.deleted_at is null
where p.organization_owner_id = :organizationId
and k.deleted_at is null
and (p.use_branching = true or k.branch_id is null)
) sub
""".trimIndent(),
).setParameter("organizationId", organizationId)
Expand Down
12 changes: 12 additions & 0 deletions backend/data/src/main/resources/db/changelog/schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5174,4 +5174,16 @@
referencedTableName="user_account" referencedColumnNames="id"
constraintName="fk_key_deleted_by_user_account"/>
</changeSet>

<!-- Partial index for org-wide key/translation count queries (fixes #3527) -->
<changeSet author="dkrizan" id="1741855200000-1" runInTransaction="false">
<sql>
create index concurrently if not exists key_project_name_ns_not_deleted
on public.key (project_id, name, namespace_id)
where deleted_at is null;
</sql>
<rollback>
drop index concurrently if exists key_project_name_ns_not_deleted;
</rollback>
</changeSet>
</databaseChangeLog>
Loading