]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-78 complete buckets of org histograms
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 26 Jun 2018 22:10:46 +0000 (00:10 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 29 Jun 2018 07:10:18 +0000 (09:10 +0200)
14 files changed:
server/sonar-db-core/src/main/java/org/sonar/db/dialect/AbstractDialect.java
server/sonar-db-core/src/main/java/org/sonar/db/dialect/Dialect.java
server/sonar-db-core/src/main/java/org/sonar/db/dialect/MySql.java
server/sonar-db-core/src/main/java/org/sonar/db/dialect/Oracle.java
server/sonar-db-core/src/test/java/org/sonar/db/dialect/H2Test.java
server/sonar-db-core/src/test/java/org/sonar/db/dialect/MsSqlTest.java
server/sonar-db-core/src/test/java/org/sonar/db/dialect/MySqlTest.java
server/sonar-db-core/src/test/java/org/sonar/db/dialect/OracleTest.java
server/sonar-db-core/src/test/java/org/sonar/db/dialect/PostgreSqlTest.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatisConfBuilder.java
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java

index f2d3466ad7742c6dae41479ccac6257b23215f2d..0bc24005796343b7ce09e31ab54638577f699aa9 100644 (file)
@@ -61,6 +61,11 @@ abstract class AbstractDialect implements Dialect {
     return falseSqlValue;
   }
 
+  @Override
+  public String getSqlFromDual() {
+    return "";
+  }
+
   @Override
   public final String getValidationQuery() {
     return validationQuery;
index 459b2c38bcf98cc6518143d2b47200be282be92d..d5ef62217ff3257120aa249f7b71b1557ff026e3 100644 (file)
@@ -56,6 +56,8 @@ public interface Dialect {
    */
   String getFalseSqlValue();
 
+  String getSqlFromDual();
+
   /**
    * Query used to validate the jdbc connection.
    *
index 8662f90fc82c5c71203f2e4fc01b38c269cf8edc..86b19993813fb1f83eba259af3ca6e3361c2d8c2 100644 (file)
@@ -51,4 +51,9 @@ public class MySql extends AbstractDialect {
   public boolean supportsMigration() {
     return true;
   }
+
+  @Override
+  public String getSqlFromDual() {
+    return "from dual";
+  }
 }
index 17afbcafe866f1df76f7848e95d4d942117badac..a55ac02157f902c8863dc3bff7c7909173fdff27 100644 (file)
@@ -49,4 +49,9 @@ public class Oracle extends AbstractDialect {
   public List<String> getConnectionInitStatements() {
     return INIT_STATEMENTS;
   }
+
+  @Override
+  public String getSqlFromDual() {
+    return "from dual";
+  }
 }
index 5ca459fe8453eb3cc1406ae4211e94506e2c2ea3..064c48fa2593b297f21c50e44f1a4caacdbec9cf 100644 (file)
@@ -25,34 +25,39 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class H2Test {
 
-  H2 dialect = new H2();
+  private H2 underTest = new H2();
 
   @Test
   public void matchesJdbcURL() {
-    assertThat(dialect.matchesJdbcURL("jdbc:h2:foo")).isTrue();
-    assertThat(dialect.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:h2:foo")).isTrue();
+    assertThat(underTest.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
   }
 
   @Test
   public void testBooleanSqlValues() {
-    assertThat(dialect.getTrueSqlValue()).isEqualTo("true");
-    assertThat(dialect.getFalseSqlValue()).isEqualTo("false");
+    assertThat(underTest.getTrueSqlValue()).isEqualTo("true");
+    assertThat(underTest.getFalseSqlValue()).isEqualTo("false");
   }
 
   @Test
   public void should_configure() {
-    assertThat(dialect.getId()).isEqualTo("h2");
-    assertThat(dialect.getDefaultDriverClassName()).isEqualTo("org.h2.Driver");
-    assertThat(dialect.getValidationQuery()).isEqualTo("SELECT 1");
+    assertThat(underTest.getId()).isEqualTo("h2");
+    assertThat(underTest.getDefaultDriverClassName()).isEqualTo("org.h2.Driver");
+    assertThat(underTest.getValidationQuery()).isEqualTo("SELECT 1");
   }
 
   @Test
   public void testFetchSizeForScrolling() {
-    assertThat(dialect.getScrollDefaultFetchSize()).isEqualTo(200);
+    assertThat(underTest.getScrollDefaultFetchSize()).isEqualTo(200);
   }
 
   @Test
   public void h2_does_not_supportMigration() {
-    assertThat(dialect.supportsMigration()).isFalse();
+    assertThat(underTest.supportsMigration()).isFalse();
+  }
+
+  @Test
+  public void getSqlFromDual() {
+    assertThat(underTest.getSqlFromDual()).isEqualTo("");
   }
 }
index 6fb49868151ac91e7b990a33961b1f894362ac13..6785f0bc126003e5158dae90dd55a7c5dfb7f605 100644 (file)
@@ -25,37 +25,42 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class MsSqlTest {
 
-  private MsSql msSql = new MsSql();
+  private MsSql underTest = new MsSql();
 
   @Test
   public void matchesJdbcURL() {
-    assertThat(msSql.matchesJdbcURL("jdbc:sqlserver://localhost:1433;databasename=sonar")).isTrue();
+    assertThat(underTest.matchesJdbcURL("jdbc:sqlserver://localhost:1433;databasename=sonar")).isTrue();
 
-    assertThat(msSql.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
-    assertThat(msSql.matchesJdbcURL("jdbc:mysql:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:mysql:foo")).isFalse();
   }
 
   @Test
   public void testBooleanSqlValues() {
-    assertThat(msSql.getTrueSqlValue()).isEqualTo("1");
-    assertThat(msSql.getFalseSqlValue()).isEqualTo("0");
+    assertThat(underTest.getTrueSqlValue()).isEqualTo("1");
+    assertThat(underTest.getFalseSqlValue()).isEqualTo("0");
   }
 
   @Test
   public void should_configure() {
-    assertThat(msSql.getId()).isEqualTo("mssql");
-    assertThat(msSql.getDefaultDriverClassName()).isEqualTo("com.microsoft.sqlserver.jdbc.SQLServerDriver");
-    assertThat(msSql.getValidationQuery()).isEqualTo("SELECT 1");
+    assertThat(underTest.getId()).isEqualTo("mssql");
+    assertThat(underTest.getDefaultDriverClassName()).isEqualTo("com.microsoft.sqlserver.jdbc.SQLServerDriver");
+    assertThat(underTest.getValidationQuery()).isEqualTo("SELECT 1");
   }
 
   @Test
   public void do_not_support_jtds_since_5_2() {
-    assertThat(msSql.matchesJdbcURL("jdbc:jtds:sqlserver://localhost;databaseName=SONAR;SelectMethod=Cursor")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:jtds:sqlserver://localhost;databaseName=SONAR;SelectMethod=Cursor")).isFalse();
 
   }
 
   @Test
   public void msSql_does_supportMigration() {
-    assertThat(msSql.supportsMigration()).isTrue();
+    assertThat(underTest.supportsMigration()).isTrue();
+  }
+
+  @Test
+  public void getSqlFromDual() {
+    assertThat(underTest.getSqlFromDual()).isEqualTo("");
   }
 }
index 039bb76bbedd4d5226c8a63a8dced9c656051262..f9004ec967995a2cff7fb71884aadd02d7a76869 100644 (file)
@@ -25,38 +25,43 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class MySqlTest {
 
-  private MySql mySql = new MySql();
+  private MySql underTest = new MySql();
 
   @Test
   public void matchesJdbcURL() {
-    assertThat(mySql.matchesJdbcURL("jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8")).isTrue();
-    assertThat(mySql.matchesJdbcURL("JDBC:MYSQL://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8")).isTrue();
+    assertThat(underTest.matchesJdbcURL("jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8")).isTrue();
+    assertThat(underTest.matchesJdbcURL("JDBC:MYSQL://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8")).isTrue();
 
-    assertThat(mySql.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
-    assertThat(mySql.matchesJdbcURL("jdbc:oracle:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:oracle:foo")).isFalse();
   }
 
   @Test
   public void testBooleanSqlValues() {
-    assertThat(mySql.getTrueSqlValue()).isEqualTo("true");
-    assertThat(mySql.getFalseSqlValue()).isEqualTo("false");
+    assertThat(underTest.getTrueSqlValue()).isEqualTo("true");
+    assertThat(underTest.getFalseSqlValue()).isEqualTo("false");
   }
 
   @Test
   public void should_configure() {
-    assertThat(mySql.getId()).isEqualTo("mysql");
-    assertThat(mySql.getDefaultDriverClassName()).isEqualTo("com.mysql.jdbc.Driver");
-    assertThat(mySql.getValidationQuery()).isEqualTo("SELECT 1");
+    assertThat(underTest.getId()).isEqualTo("mysql");
+    assertThat(underTest.getDefaultDriverClassName()).isEqualTo("com.mysql.jdbc.Driver");
+    assertThat(underTest.getValidationQuery()).isEqualTo("SELECT 1");
   }
 
   @Test
   public void testFetchSizeForScrolling() {
-    assertThat(mySql.getScrollDefaultFetchSize()).isEqualTo(Integer.MIN_VALUE);
-    assertThat(mySql.getScrollSingleRowFetchSize()).isEqualTo(Integer.MIN_VALUE);
+    assertThat(underTest.getScrollDefaultFetchSize()).isEqualTo(Integer.MIN_VALUE);
+    assertThat(underTest.getScrollSingleRowFetchSize()).isEqualTo(Integer.MIN_VALUE);
   }
 
   @Test
   public void mysql_does_supportMigration() {
-    assertThat(mySql.supportsMigration()).isTrue();
+    assertThat(underTest.supportsMigration()).isTrue();
+  }
+
+  @Test
+  public void getSqlFromDual() {
+    assertThat(underTest.getSqlFromDual()).isEqualTo("from dual");
   }
 }
index 3e7d55613b6044cb9de10e8526ef190004fb47b4..f2a2e3a502abc0f58f15f1fed49338b066f464cc 100644 (file)
@@ -56,4 +56,9 @@ public class OracleTest {
   public void oracle_does_supportMigration() {
     assertThat(underTest.supportsMigration()).isTrue();
   }
+
+  @Test
+  public void getSqlFromDual() {
+    assertThat(underTest.getSqlFromDual()).isEqualTo("from dual");
+  }
 }
index 67dd06a7fdd0245e5363b8667f1c903db2948a11..ef2ffadd329ae4e9b3e16c34eddca747935fb878 100644 (file)
@@ -25,39 +25,44 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class PostgreSqlTest {
 
-  PostgreSql dialect = new PostgreSql();
+  private PostgreSql underTest = new PostgreSql();
 
   @Test
   public void matchesJdbcURL() {
-    assertThat(dialect.matchesJdbcURL("jdbc:postgresql://localhost/sonar")).isTrue();
-    assertThat(dialect.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
+    assertThat(underTest.matchesJdbcURL("jdbc:postgresql://localhost/sonar")).isTrue();
+    assertThat(underTest.matchesJdbcURL("jdbc:hsql:foo")).isFalse();
   }
 
   @Test
   public void should_set_connection_properties() {
-    assertThat(dialect.getConnectionInitStatements()).isEqualTo(PostgreSql.INIT_STATEMENTS);
+    assertThat(underTest.getConnectionInitStatements()).isEqualTo(PostgreSql.INIT_STATEMENTS);
   }
 
   @Test
   public void testBooleanSqlValues() {
-    assertThat(dialect.getTrueSqlValue()).isEqualTo("true");
-    assertThat(dialect.getFalseSqlValue()).isEqualTo("false");
+    assertThat(underTest.getTrueSqlValue()).isEqualTo("true");
+    assertThat(underTest.getFalseSqlValue()).isEqualTo("false");
   }
 
   @Test
   public void should_configure() {
-    assertThat(dialect.getId()).isEqualTo("postgresql");
-    assertThat(dialect.getDefaultDriverClassName()).isEqualTo("org.postgresql.Driver");
-    assertThat(dialect.getValidationQuery()).isEqualTo("SELECT 1");
+    assertThat(underTest.getId()).isEqualTo("postgresql");
+    assertThat(underTest.getDefaultDriverClassName()).isEqualTo("org.postgresql.Driver");
+    assertThat(underTest.getValidationQuery()).isEqualTo("SELECT 1");
   }
 
   @Test
   public void testFetchSizeForScrolling() {
-    assertThat(dialect.getScrollDefaultFetchSize()).isEqualTo(200);
+    assertThat(underTest.getScrollDefaultFetchSize()).isEqualTo(200);
   }
 
   @Test
   public void postgres_does_supportMigration() {
-    assertThat(dialect.supportsMigration()).isTrue();
+    assertThat(underTest.supportsMigration()).isTrue();
+  }
+
+  @Test
+  public void getSqlFromDual() {
+    assertThat(underTest.getSqlFromDual()).isEqualTo("");
   }
 }
index 0a1a5dcf5cdbb1a1b729f7a3e3101b083085e1b0..e16ce4523a47f14dd2a49755beda43122ddd8091 100644 (file)
@@ -46,6 +46,7 @@ class MyBatisConfBuilder {
     this.conf.setDatabaseId(dialect.getId());
     this.conf.getVariables().setProperty("_true", dialect.getTrueSqlValue());
     this.conf.getVariables().setProperty("_false", dialect.getFalseSqlValue());
+    this.conf.getVariables().setProperty("_from_dual", dialect.getSqlFromDual());
     this.conf.getVariables().setProperty("_scrollFetchSize", String.valueOf(dialect.getScrollDefaultFetchSize()));
     this.conf.setLocalCacheScope(LocalCacheScope.STATEMENT);
   }
index 58fc0cd8c59046e7cddae4d277e3e319b355aead..c7f3623f7fdebff4c28f96eae8b11ae54c72e7f0 100644 (file)
@@ -62,6 +62,10 @@ public class OrganizationDao implements Dao {
     return getMapper(dbSession).countTeamsByProjects();
   }
 
+  public List<KeyLongValue> countTeamsByNclocRanges(DbSession dbSession) {
+    return getMapper(dbSession).countTeamsByNclocRanges();
+  }
+
   public List<OrganizationDto> selectByQuery(DbSession dbSession, OrganizationQuery organizationQuery, Pagination pagination) {
     requireNonNull(organizationQuery, "organizationQuery can't be null");
     return getMapper(dbSession).selectByQuery(organizationQuery, pagination);
index e3ce1865389260c0da33af73a6d5331a10a80151..af95fabb58d8cfdf4bc8e8c5f95612055579dc30 100644 (file)
@@ -34,6 +34,8 @@ public interface OrganizationMapper {
 
   List<KeyLongValue> countTeamsByProjects();
 
+  List<KeyLongValue> countTeamsByNclocRanges();
+
   List<OrganizationDto> selectByQuery(@Param("query") OrganizationQuery organizationQuery,
     @Param("pagination") Pagination pagination);
 
index 74578417dc7b9c2c5252f5e82240873a15d114c4..81a89be544992cf9d9650b2cdbc2d6798ea08c40 100644 (file)
   </sql>
 
   <select id="countTeamsByMembers" resultType="org.sonar.db.KeyLongValue">
-    select range as "key", count(1) as "value"
+    select kee as "key", sum(val) as "value"
     from (
-      select case
-          when nb = 0 then '0'
-          when nb = 1 then '1'
-          when nb &gt;= 2 and nb &lt;= 4 then '2-4'
-          when nb &gt;= 5 and nb &lt;= 9 then '5-9'
-          when nb &gt;= 10 and nb &lt;= 24 then '10-24'
-          else '25+'
-        end as range
+      select '0' as kee, 0 as val ${_from_dual}
+      union
+      select '1' as kee, 0 as val ${_from_dual}
+      union
+      select '2-4' as kee, 0 as val ${_from_dual}
+      union
+      select '5-9' as kee, 0 as val ${_from_dual}
+      union
+      select '10-24' as kee, 0 as val ${_from_dual}
+      union
+      select '+25' as kee, 0 as val ${_from_dual}
+      union
+      select kee, count(1) as val
       from (
-        select o.uuid, count(om.user_id) as nb
-        from organizations o
-        left join organization_members om on om.organization_uuid = o.uuid
-        where not exists(
-          select 1
-          from users u
-          where u.organization_uuid = o.uuid
-          and u.active = ${_true}
-        )
-        group by o.uuid
-      ) alias1
-    ) alias2
-    group by range
+        select case
+            when nb = 0 then '0'
+            when nb = 1 then '1'
+            when nb &gt;= 2 and nb &lt;= 4 then '2-4'
+            when nb &gt;= 5 and nb &lt;= 9 then '5-9'
+            when nb &gt;= 10 and nb &lt;= 24 then '10-24'
+            else '+25'
+          end as kee
+        from (
+          select o.uuid, count(om.user_id) as nb
+          from organizations o
+          left join organization_members om on om.organization_uuid = o.uuid
+          where not exists(
+            select 1
+            from users u
+            where u.organization_uuid = o.uuid
+            and u.active = ${_true}
+          )
+          group by o.uuid
+        ) alias1
+      ) alias2
+      group by kee
+    ) alias3
+    group by kee
   </select>
 
   <select id="countTeamsByProjects" resultType="org.sonar.db.KeyLongValue">
-    select range as "key", count(1) as "value"
+    select kee as "key", sum(val) as "value"
     from (
-      select case
-          when nb = 0 then '0'
-          when nb = 1 then '1'
-          when nb &gt;= 2 and nb &lt;= 4 then '2-4'
-          when nb &gt;= 5 and nb &lt;= 9 then '5-9'
-          when nb &gt;= 10 and nb &lt;= 24 then '10-24'
-          else '25+'
-        end as range
+      select '0' as kee, 0 as val ${_from_dual}
+      union
+      select '1' as kee, 0 as val ${_from_dual}
+      union
+      select '2-4' as kee, 0 as val ${_from_dual}
+      union
+      select '5-9' as kee, 0 as val ${_from_dual}
+      union
+      select '10-24' as kee, 0 as val ${_from_dual}
+      union
+      select '+25' as kee, 0 as val ${_from_dual}
+      union
+      select kee, count(1) as val
       from (
-        select o.uuid, count(p.uuid) as nb
-        from organizations o
-        left join projects p on p.organization_uuid = o.uuid
-        where not exists(
-          select 1
-          from users u
-          where u.organization_uuid = o.uuid
-          and u.active = ${_true}
-        )
-        group by o.uuid
-      ) alias1
-    ) alias2
-    group by range
+        select case
+            when nb = 0 then '0'
+            when nb = 1 then '1'
+            when nb &gt;= 2 and nb &lt;= 4 then '2-4'
+            when nb &gt;= 5 and nb &lt;= 9 then '5-9'
+            when nb &gt;= 10 and nb &lt;= 24 then '10-24'
+            else '+25'
+          end as kee
+        from (
+          select o.uuid, count(p.uuid) as nb
+          from organizations o
+          left join projects p on p.organization_uuid = o.uuid
+          where not exists(
+            select 1
+            from users u
+            where u.organization_uuid = o.uuid
+            and u.active = ${_true}
+          )
+          group by o.uuid
+        ) alias1
+      ) alias2
+      group by kee
+    ) alias3
+    group by kee
+  </select>
+
+  <select id="countTeamsByNclocRanges" resultType="org.sonar.db.KeyLongValue">
+    select kee as "key", sum(val) as "value"
+    from (
+      select '100K' as kee, 0 as val ${_from_dual}
+      union
+      select '500K' as kee, 0 as val ${_from_dual}
+      union
+      select '1M' as kee, 0 as val ${_from_dual}
+      union
+      select '2M' as kee, 0 as val ${_from_dual}
+      union
+      select '5M' as kee, 0 as val ${_from_dual}
+      union
+      select '10M' as kee, 0 as val ${_from_dual}
+      union
+      select '20M' as kee, 0 as val ${_from_dual}
+      union
+      select '50M' as kee, 0 as val ${_from_dual}
+      union
+      select '+50M' as kee, 0 as val ${_from_dual}
+      union
+      select kee, count(1) as val
+      from (
+        select case
+          when locs &lt;= 100000 then '100K'
+          when locs &gt; 100000 and locs &lt;= 500000 then '500K'
+          when locs &gt; 500000 and locs &lt;= 1000000 then '1M'
+          when locs &gt; 1000000 and locs &lt;= 2000000 then '2M'
+          when locs &gt; 2000000 and locs &lt;= 5000000 then '5M'
+          when locs &gt; 5000000 and locs &lt;= 10000000 then '10M'
+          when locs &gt; 10000000 and locs &lt;= 20000000 then '20M'
+          when locs &gt; 20000000 and locs &lt;= 50000000 then '50M'
+          else '+50M'
+          end as kee
+        from (
+          select p.organization_uuid as orgUuid, max(lm.value) as locs
+          from live_measures lm
+          inner join metrics m on m.id = lm.metric_id
+          inner join projects p on p.uuid = lm.component_uuid
+          inner join project_branches b on b.uuid = p.uuid
+          where
+            m.name = 'ncloc'
+            and p.enabled = ${_true}
+            and p.scope = 'PRJ'
+            and p.qualifier = 'TRK'
+            and p.copy_component_uuid is null
+            and b.branch_type = 'LONG'
+            and b.key_type = 'BRANCH'
+            -- team orgs
+            and not exists(
+              select 1
+              from users u
+              where u.organization_uuid = p.organization_uuid
+              and u.active = ${_true}
+            )
+          group by p.organization_uuid
+        ) alias1
+      ) alias2
+      group by kee
+    ) alias3
+    group by kee
+
   </select>
 
   <select id="selectByPermission" parameterType="map" resultType="Organization">
index 2c3c8f810611c3fe73e41228a4be896bbf4c3de3..100fa452b735817d740b3d746f452675a3622cf5 100644 (file)
@@ -33,11 +33,11 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.apache.ibatis.exceptions.PersistenceException;
-import org.assertj.core.groups.Tuple;
 import org.assertj.core.util.Lists;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -47,6 +47,7 @@ import org.sonar.db.Pagination;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.dialect.Dialect;
 import org.sonar.db.dialect.Oracle;
+import org.sonar.db.metric.MetricDto;
 import org.sonar.db.qualitygate.QGateWithOrgDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.GroupTesting;
@@ -976,9 +977,20 @@ public class OrganizationDaoTest {
   }
 
   @Test
-  public void countTeamsByMembers() {
-    assertThat(underTest.countTeamsByMembers(dbSession)).isEmpty();
+  public void countTeamsByMembers_on_zero_orgs() {
+    assertThat(underTest.countTeamsByMembers(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(
+        tuple("0", 0L),
+        tuple("1", 0L),
+        tuple("2-4", 0L),
+        tuple("5-9", 0L),
+        tuple("10-24", 0L),
+        tuple("+25", 0L));
+  }
 
+  @Test
+  public void countTeamsByMembers() {
     UserDto user1 = db.users().insertUser();
     UserDto user2 = db.users().insertUser();
     UserDto user3 = db.users().insertUser();
@@ -987,18 +999,34 @@ public class OrganizationDaoTest {
     OrganizationDto org2 = db.organizations().insert();
     db.organizations().addMember(org2, user1);
     OrganizationDto org3 = db.organizations().insert();
-    db.organizations().addMember(org3, user1, user2, user3);
+    db.organizations().addMember(org3, user1, user2);
 
     assertThat(underTest.countTeamsByMembers(dbSession))
       .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
-      .containsExactlyInAnyOrder(Tuple.tuple("1", 1L), Tuple.tuple("2-4", 2L));
+      .containsExactlyInAnyOrder(
+        tuple("0", 0L),
+        tuple("1", 1L),
+        tuple("2-4", 2L),
+        tuple("5-9", 0L),
+        tuple("10-24", 0L),
+        tuple("+25", 0L));
+  }
 
+  @Test
+  public void countTeamsByProjects_on_zero_projects() {
+    assertThat(underTest.countTeamsByProjects(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(
+        tuple("0", 0L),
+        tuple("1", 0L),
+        tuple("2-4", 0L),
+        tuple("5-9", 0L),
+        tuple("10-24", 0L),
+        tuple("+25", 0L));
   }
 
   @Test
   public void countTeamsByProjects() {
-    assertThat(underTest.countTeamsByProjects(dbSession)).isEmpty();
-
     OrganizationDto org1 = db.organizations().insert();
     db.components().insertPrivateProject(org1);
     OrganizationDto org2 = db.organizations().insert();
@@ -1011,8 +1039,64 @@ public class OrganizationDaoTest {
 
     assertThat(underTest.countTeamsByProjects(dbSession))
       .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
-      .containsExactlyInAnyOrder(Tuple.tuple("1", 1L), Tuple.tuple("2-4", 2L));
+      .containsExactlyInAnyOrder(
+        tuple("0", 0L),
+        tuple("1", 1L),
+        tuple("2-4", 2L),
+        tuple("5-9", 0L),
+        tuple("10-24", 0L),
+        tuple("+25", 0L));
+  }
 
+  @Test
+  public void countTeamsByNclocRanges() {
+    MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(CoreMetrics.NCLOC_KEY));
+
+    OrganizationDto org1 = db.organizations().insert();
+    // project with highest ncloc in non-main branch
+    ComponentDto project1 = db.components().insertMainBranch(org1);
+    ComponentDto project1Branch = db.components().insertProjectBranch(project1);
+    db.measures().insertLiveMeasure(project1, ncloc, m -> m.setValue(1_000.0));
+    db.measures().insertLiveMeasure(project1Branch, ncloc, m -> m.setValue(110_000.0));
+    // project with only main branch
+    ComponentDto project2 = db.components().insertMainBranch(org1);
+    db.measures().insertLiveMeasure(project2, ncloc, m -> m.setValue(400_000.0));
+
+    OrganizationDto org2 = db.organizations().insert();
+    // project with highest ncloc in main branch
+    ComponentDto project3 = db.components().insertMainBranch(org2);
+    ComponentDto project3Branch = db.components().insertProjectBranch(project3);
+    db.measures().insertLiveMeasure(project3, ncloc, m -> m.setValue(5_800_000.0));
+    db.measures().insertLiveMeasure(project3Branch, ncloc, m -> m.setValue(25_000.0));
+
+    assertThat(underTest.countTeamsByNclocRanges(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(
+        tuple("100K", 0L),
+        tuple("500K", 1L),
+        tuple("1M", 0L),
+        tuple("2M", 0L),
+        tuple("5M", 0L),
+        tuple("10M", 1L),
+        tuple("20M", 0L),
+        tuple("50M", 0L),
+        tuple("+50M", 0L));
+  }
+
+  @Test
+  public void countTeamsByNclocRanges_on_zero_orgs() {
+    assertThat(underTest.countTeamsByNclocRanges(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(
+        tuple("100K", 0L),
+        tuple("500K", 0L),
+        tuple("1M", 0L),
+        tuple("2M", 0L),
+        tuple("5M", 0L),
+        tuple("10M", 0L),
+        tuple("20M", 0L),
+        tuple("50M", 0L),
+        tuple("+50M", 0L));
   }
 
   private void expectDtoCanNotBeNull() {