import { TargetAccessScope, ProjectType, ProjectAccessScope, OrganizationAccessScope } from '@app/gql/graphql';
import formatISO from 'date-fns/formatISO';
import subHours from 'date-fns/subHours';
import {
  createOrganization,
  createProject,
  createTarget,
  createToken,
  publishSchema,
  checkSchema,
  setTargetValidation,
  updateTargetValidationSettings,
  readOperationsStats,
  waitFor,
} from '../../../testkit/flow';
import { authenticate } from '../../../testkit/auth';
import { collect, CollectedOperation } from '../../../testkit/usage';
import { clickHouseQuery } from '../../../testkit/clickhouse';
// eslint-disable-next-line hive/enforce-deps-in-dev, import/no-extraneous-dependencies
import { normalizeOperation } from '@graphql-hive/core';
// eslint-disable-next-line import/no-extraneous-dependencies
import { parse, print } from 'graphql';

function ensureNumber(value: number | string): number {
  if (typeof value === 'number') {
    return value;
  }

  return parseFloat(value);
}

// eslint-disable-next-line no-process-env
const FF_CLICKHOUSE_V2_TABLES = process.env.FF_CLICKHOUSE_V2_TABLES === '1';

if (FF_CLICKHOUSE_V2_TABLES) {
  console.log('Using FF_CLICKHOUSE_V2_TABLES');
}

function sendBatch(amount: number, operation: CollectedOperation, token: string) {
  return collect({
    operations: new Array(amount).fill(operation),
    token,
  });
}

test('collect operation', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const settingsTokenResult = await createToken(
    {
      name: 'test-settings',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.Settings],
    },
    owner_access_token
  );

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(settingsTokenResult.body.errors).not.toBeDefined();
  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;
  const tokenForSettings = settingsTokenResult.body.data!.createToken.ok!.secret;

  const schemaPublishResult = await publishSchema(
    {
      author: 'Kamil',
      commit: 'abc123',
      sdl: `type Query { ping: String me: String }`,
    },
    token
  );

  expect(schemaPublishResult.body.errors).not.toBeDefined();
  expect((schemaPublishResult.body.data!.schemaPublish as any).valid).toEqual(true);

  const targetValidationResult = await setTargetValidation(
    {
      enabled: true,
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
    },
    {
      token: tokenForSettings,
    }
  );

  expect(targetValidationResult.body.errors).not.toBeDefined();
  expect(targetValidationResult.body.data!.setTargetValidation.enabled).toEqual(true);
  expect(targetValidationResult.body.data!.setTargetValidation.percentage).toEqual(0);
  expect(targetValidationResult.body.data!.setTargetValidation.period).toEqual(30);

  // should not be breaking because the field is unused
  const unusedCheckResult = await checkSchema(
    {
      sdl: `type Query { me: String }`,
    },
    token
  );
  expect(unusedCheckResult.body.errors).not.toBeDefined();
  expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckSuccess');

  const collectResult = await collect({
    operations: [
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  expect(collectResult.status).toEqual(200);

  await waitFor(5_000);

  // should be breaking because the field is used now
  const usedCheckResult = await checkSchema(
    {
      sdl: `type Query { me: String }`,
    },
    token
  );

  if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckError') {
    throw new Error(`Expected SchemaCheckError, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
  }

  expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(false);

  const from = formatISO(subHours(Date.now(), 6));
  const to = formatISO(Date.now());
  const operationStatsResult = await readOperationsStats(
    {
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      period: {
        from,
        to,
      },
    },
    token
  );

  expect(operationStatsResult.body.errors).not.toBeDefined();

  const operationsStats = operationStatsResult.body.data!.operationsStats;

  expect(operationsStats.operations.nodes).toHaveLength(1);

  const op = operationsStats.operations.nodes[0];

  expect(op.count).toEqual(1);
  expect(op.document).toMatch('ping');
  expect(op.operationHash).toBeDefined();
  expect(op.duration.p75).toEqual(200);
  expect(op.duration.p90).toEqual(200);
  expect(op.duration.p95).toEqual(200);
  expect(op.duration.p99).toEqual(200);
  expect(op.kind).toEqual('query');
  expect(op.name).toMatch('ping');
  expect(op.percentage).toBeGreaterThan(99);
});

test('normalize and collect operation without breaking its syntax', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const settingsTokenResult = await createToken(
    {
      name: 'test-settings',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.Settings],
    },
    owner_access_token
  );

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(settingsTokenResult.body.errors).not.toBeDefined();
  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  const raw_document = `
    query outfit {
      recommendations(
        input: {
          strategies: [{ name: "asd" }]
          articleId: "asd"
          customerId: "asd"
          phoenixEnabled: true
          sessionId: "asd"
        }
      ) {
        ... on RecommendationResponse {
          frequentlyBoughtTogether {
            recommendedProducts {
              id
            }
            strategyMessage
          }
          outfit {
            strategyMessage
          }
          outfit {
            recommendedProducts {
              articleId
              id
              imageUrl
              name
              productUrl
              rating
              tCode
            }
            strategyMessage
          }
          similar {
            recommendedProducts {
              articleId
              id
              imageUrl
              name
              productUrl
              rating
              tCode
            }
            strategyMessage
          }
          visualSearch {
            strategyMessage
          }
        }
      }
    }
  `;

  const normalized_document = normalizeOperation({
    document: parse(raw_document),
    operationName: 'outfit',
    hideLiterals: true,
    removeAliases: true,
  });

  const collectResult = await collect({
    operations: [
      {
        operation: normalizeOperation({
          document: parse(raw_document),
          operationName: 'outfit',
          hideLiterals: true,
          removeAliases: true,
        }),
        operationName: 'outfit',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  expect(collectResult.status).toEqual(200);

  await waitFor(5_000);

  const from = formatISO(subHours(Date.now(), 6));
  const to = formatISO(Date.now());
  const operationStatsResult = await readOperationsStats(
    {
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      period: {
        from,
        to,
      },
    },
    token
  );

  expect(operationStatsResult.body.errors).not.toBeDefined();

  const operationsStats = operationStatsResult.body.data!.operationsStats;

  expect(operationsStats.operations.nodes).toHaveLength(1);

  const op = operationsStats.operations.nodes[0];

  expect(op.count).toEqual(1);
  expect(() => {
    parse(op.document);
  }).not.toThrow();
  expect(print(parse(op.document))).toEqual(print(parse(normalized_document)));
  expect(op.operationHash).toBeDefined();
  expect(op.duration.p75).toEqual(200);
  expect(op.duration.p90).toEqual(200);
  expect(op.duration.p95).toEqual(200);
  expect(op.duration.p99).toEqual(200);
  expect(op.kind).toEqual('query');
  expect(op.name).toMatch('outfit');
  expect(op.percentage).toBeGreaterThan(99);
});

test('number of produced and collected operations should match (no errors)', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  const batchSize = 1000;
  const totalAmount = 10_000;
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  for await (const _ of new Array(totalAmount / batchSize)) {
    await sendBatch(
      batchSize,
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      token
    );
  }

  await waitFor(5_000);

  const from = formatISO(subHours(Date.now(), 6));
  const to = formatISO(Date.now());
  const operationStatsResult = await readOperationsStats(
    {
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      period: {
        from,
        to,
      },
    },
    token
  );

  expect(operationStatsResult.body.errors).not.toBeDefined();

  const operationsStats = operationStatsResult.body.data!.operationsStats;

  // We sent a single operation (multiple times)
  expect(operationsStats.operations.nodes).toHaveLength(1);

  const op = operationsStats.operations.nodes[0];

  expect(op.count).toEqual(totalAmount);
  expect(op.document).toMatch('ping');
  expect(op.operationHash).toBeDefined();
  expect(op.duration.p75).toEqual(200);
  expect(op.duration.p90).toEqual(200);
  expect(op.duration.p95).toEqual(200);
  expect(op.duration.p99).toEqual(200);
  expect(op.kind).toEqual('query');
  expect(op.name).toMatch('ping');
  expect(op.percentage).toBeGreaterThan(99);
});

test('check usage from two selected targets', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const staging = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const productionTargetResult = await createTarget(
    {
      name: 'production',
      organization: org.cleanId,
      project: project.cleanId,
    },
    owner_access_token
  );

  const production = productionTargetResult.body.data!.createTarget.ok!.createdTarget;

  const stagingTokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: staging.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  const productionTokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: production.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(stagingTokenResult.body.errors).not.toBeDefined();
  expect(productionTokenResult.body.errors).not.toBeDefined();

  const tokenForStaging = stagingTokenResult.body.data!.createToken.ok!.secret;
  const tokenForProduction = productionTokenResult.body.data!.createToken.ok!.secret;

  const schemaPublishResult = await publishSchema(
    {
      author: 'Kamil',
      commit: 'usage-check-2',
      sdl: `type Query { ping: String me: String }`,
    },
    tokenForStaging
  );

  expect(schemaPublishResult.body.errors).not.toBeDefined();
  expect((schemaPublishResult.body.data!.schemaPublish as any).valid).toEqual(true);

  const targetValidationResult = await setTargetValidation(
    {
      enabled: true,
      organization: org.cleanId,
      project: project.cleanId,
      target: staging.cleanId,
    },
    {
      authToken: owner_access_token,
    }
  );

  expect(targetValidationResult.body.errors).not.toBeDefined();
  expect(targetValidationResult.body.data!.setTargetValidation.enabled).toEqual(true);
  expect(targetValidationResult.body.data!.setTargetValidation.percentage).toEqual(0);
  expect(targetValidationResult.body.data!.setTargetValidation.period).toEqual(30);

  const collectResult = await collect({
    operations: [
      {
        timestamp: Date.now(),
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
        metadata: {},
      },
      {
        timestamp: Date.now(),
        operation: 'query me { me }',
        operationName: 'me',
        fields: ['Query', 'Query.me'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        timestamp: Date.now(),
        operation: 'query me { me }',
        operationName: 'me',
        fields: ['Query', 'Query.me'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token: tokenForProduction, // put collected operation in production
  });

  expect(collectResult.status).toEqual(200);

  await waitFor(5_000);

  // should not be breaking because the field is unused on staging
  const unusedCheckResult = await checkSchema(
    {
      sdl: `type Query { me: String }`, // ping is used but on production
    },
    tokenForStaging
  );
  expect(unusedCheckResult.body.errors).not.toBeDefined();
  expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckSuccess');

  // Now switch to using checking both staging and production

  const updateValidationResult = await updateTargetValidationSettings(
    {
      organization: org.cleanId,
      project: project.cleanId,
      target: staging.cleanId,
      percentage: 50, // Out of 3 requests, 1 is for Query.me, 2 are done for Query.me so it's 1/3 = 33.3%
      period: 2,
      targets: [production.id, staging.id],
    },
    {
      authToken: owner_access_token,
    }
  );

  expect(updateValidationResult.body.errors).not.toBeDefined();
  expect(updateValidationResult.body.data!.updateTargetValidationSettings.error).toBeNull();
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.percentage
  ).toEqual(50);
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.period
  ).toEqual(2);
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.targets
  ).toHaveLength(2);

  // should be non-breaking because the field is used in production and we are checking staging and production now
  // and it used in less than 50% of traffic
  const usedCheckResult = await checkSchema(
    {
      sdl: `type Query { me: String }`, // ping is used on production and we do check production now
    },
    tokenForStaging
  );

  if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckSuccess') {
    throw new Error(`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
  }

  expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(true);
  expect(usedCheckResult.body.errors).not.toBeDefined();
});

test('check usage not from excluded client names', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const production = projectResult.body.data!.createProject.ok!.createdTargets.find(t => t.name === 'production');

  if (!production) {
    throw new Error('No production target');
  }

  const productionTokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: production.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(productionTokenResult.body.errors).not.toBeDefined();

  const tokenForProduction = productionTokenResult.body.data!.createToken.ok!.secret;

  const schemaPublishResult = await publishSchema(
    {
      author: 'Kamil',
      commit: 'usage-check-2',
      sdl: `type Query { ping: String me: String }`,
    },
    tokenForProduction
  );

  expect(schemaPublishResult.body.errors).not.toBeDefined();
  expect((schemaPublishResult.body.data!.schemaPublish as any).valid).toEqual(true);

  const targetValidationResult = await setTargetValidation(
    {
      enabled: true,
      organization: org.cleanId,
      project: project.cleanId,
      target: production.cleanId,
    },
    {
      authToken: owner_access_token,
    }
  );

  expect(targetValidationResult.body.errors).not.toBeDefined();
  expect(targetValidationResult.body.data!.setTargetValidation.enabled).toEqual(true);
  expect(targetValidationResult.body.data!.setTargetValidation.percentage).toEqual(0);
  expect(targetValidationResult.body.data!.setTargetValidation.period).toEqual(30);

  const collectResult = await collect({
    operations: [
      {
        timestamp: Date.now(),
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
        metadata: {
          client: {
            name: 'cli',
            version: '2.0.0',
          },
        },
      },
      {
        timestamp: Date.now(),
        operation: 'query me { me }',
        operationName: 'me',
        fields: ['Query', 'Query.me'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
        metadata: {
          client: {
            name: 'app',
            version: '1.0.0',
          },
        },
      },
      {
        timestamp: Date.now(),
        operation: 'query me { me }',
        operationName: 'me',
        fields: ['Query', 'Query.me'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
        metadata: {
          client: {
            name: 'app',
            version: '1.0.1',
          },
        },
      },
    ],
    token: tokenForProduction,
  });

  expect(collectResult.status).toEqual(200);

  await waitFor(5_000);

  // should be breaking because the field is used
  const unusedCheckResult = await checkSchema(
    {
      sdl: `type Query { ping: String }`, // Query.me is used
    },
    tokenForProduction
  );
  expect(unusedCheckResult.body.errors).not.toBeDefined();
  expect(unusedCheckResult.body.data!.schemaCheck.__typename).toEqual('SchemaCheckError');

  // Exclude app from the check
  const updateValidationResult = await updateTargetValidationSettings(
    {
      organization: org.cleanId,
      project: project.cleanId,
      target: production.cleanId,
      percentage: 0,
      period: 2,
      targets: [production.id],
      excludedClients: ['app'],
    },
    {
      authToken: owner_access_token,
    }
  );

  expect(updateValidationResult.body.errors).not.toBeDefined();
  expect(updateValidationResult.body.data!.updateTargetValidationSettings.error).toBeNull();
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.enabled
  ).toBe(true);
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.excludedClients
  ).toHaveLength(1);
  expect(
    updateValidationResult.body.data!.updateTargetValidationSettings.ok!.updatedTargetValidationSettings.excludedClients
  ).toContainEqual('app');

  // should be safe because the field was not used by the non-excluded clients (cli never requested `Query.me`, but app did)
  const usedCheckResult = await checkSchema(
    {
      sdl: `type Query { ping: String }`,
    },
    tokenForProduction
  );

  if (usedCheckResult.body.data!.schemaCheck.__typename !== 'SchemaCheckSuccess') {
    throw new Error(`Expected SchemaCheckSuccess, got ${usedCheckResult.body.data!.schemaCheck.__typename}`);
  }

  expect(usedCheckResult.body.data!.schemaCheck.valid).toEqual(true);
  expect(usedCheckResult.body.errors).not.toBeDefined();
});

test('number of produced and collected operations should match', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  const batchSize = 1000;
  const totalAmount = 10_000;
  for await (const i of new Array(totalAmount / batchSize).fill(null).map((_, i) => i)) {
    await sendBatch(
      batchSize,
      i % 2 === 0
        ? {
            operation: 'query ping { ping }',
            operationName: 'ping',
            fields: ['Query', 'Query.ping'],
            execution: {
              ok: true,
              duration: 200000000,
              errorsTotal: 0,
            },
          }
        : {
            operation: 'query ping { ping }',
            operationName: 'ping',
            fields: ['Query', 'Query.ping'],
            execution: {
              ok: true,
              duration: 200000000,
              errorsTotal: 0,
            },
            metadata: {
              client: {
                name: 'web',
                version: '1.2.3',
              },
            },
          },
      token
    );
  }

  await waitFor(5_000);

  const result = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(`
    SELECT
      target, client_name, hash, sum(total) as total
    FROM ${FF_CLICKHOUSE_V2_TABLES ? 'clients_daily' : 'client_names_daily'}
    WHERE 
      timestamp >= subtractDays(now(), 30)
      AND timestamp <= now()
    GROUP BY target, client_name, hash
  `);

  expect(result.rows).toEqual(2);
  expect(result.data).toContainEqual(
    expect.objectContaining({
      target: target.id,
      client_name: 'web',
      hash: expect.any(String),
      total: expect.stringMatching('5000'),
    })
  );
  expect(result.data).toContainEqual(
    expect.objectContaining({
      target: target.id,
      client_name: '',
      hash: expect.any(String),
      total: expect.stringMatching('5000'),
    })
  );
});

test('different order of schema coordinates should not result in different hash', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  await collect({
    operations: [
      {
        operation: 'query ping {        ping      }', // those spaces are expected and important to ensure normalization is in place
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query.ping', 'Query'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  await waitFor(5_000);

  const coordinatesResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(`
    SELECT coordinate, hash FROM ${
      FF_CLICKHOUSE_V2_TABLES ? 'coordinates_daily' : 'schema_coordinates_daily'
    } GROUP BY coordinate, hash
  `);

  expect(coordinatesResult.rows).toEqual(2);

  const operationCollectionResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(
    FF_CLICKHOUSE_V2_TABLES
      ? `SELECT hash FROM operation_collection GROUP BY hash`
      : `SELECT hash FROM operations_registry FINAL GROUP BY hash`
  );

  expect(operationCollectionResult.rows).toEqual(1);
});

test('same operation but with different schema coordinates should result in different hash', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  await collect({
    operations: [
      {
        operation: 'query ping {        ping      }', // those spaces are expected and important to ensure normalization is in place
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['RootQuery', 'RootQuery.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  await waitFor(5_000);

  const coordinatesResult = await clickHouseQuery<{
    coordinate: string;
    hash: string;
  }>(`
    SELECT coordinate, hash FROM ${
      FF_CLICKHOUSE_V2_TABLES ? 'coordinates_daily' : 'schema_coordinates_daily'
    } GROUP BY coordinate, hash
  `);

  expect(coordinatesResult.rows).toEqual(4);

  const operationCollectionResult = await clickHouseQuery<{
    hash: string;
  }>(
    FF_CLICKHOUSE_V2_TABLES
      ? `SELECT hash FROM operation_collection GROUP BY hash`
      : `SELECT hash FROM operations_registry FINAL GROUP BY hash`
  );

  expect(operationCollectionResult.rows).toEqual(2);

  const operationsResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(
    FF_CLICKHOUSE_V2_TABLES
      ? `SELECT hash FROM operation_collection GROUP BY hash`
      : `SELECT hash FROM operations_registry FINAL GROUP BY hash`
  );

  expect(operationsResult.rows).toEqual(2);
});

test('operations with the same schema coordinates and body but with different name should result in different hashes', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  await collect({
    operations: [
      {
        operation: 'query pingv2 { ping }',
        operationName: 'pingv2',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  await waitFor(5_000);

  const coordinatesResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(`
    SELECT coordinate, hash FROM ${
      FF_CLICKHOUSE_V2_TABLES ? 'coordinates_daily' : 'schema_coordinates_daily'
    } GROUP BY coordinate, hash
  `);

  expect(coordinatesResult.rows).toEqual(4);

  const operationsResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(
    FF_CLICKHOUSE_V2_TABLES
      ? `SELECT hash FROM operation_collection GROUP BY hash`
      : `SELECT hash FROM operations_registry FINAL GROUP BY hash`
  );

  expect(operationsResult.rows).toEqual(2);
});

test('ignore operations with syntax errors', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  const collectResult = await collect({
    operations: [
      {
        operation: 'query pingv2 { pingv2 }',
        operationName: 'pingv2',
        fields: ['Query', 'Query.pingv2'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        operation: 'query ping ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
    ],
    token,
  });

  expect(collectResult.status).toEqual(200);
  expect(typeof collectResult.body !== 'string' && collectResult.body.operations).toEqual(
    expect.objectContaining({
      rejected: 1,
      accepted: 1,
    })
  );

  await waitFor(5_000);

  const coordinatesResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(`
    SELECT coordinate, hash FROM ${
      FF_CLICKHOUSE_V2_TABLES ? 'coordinates_daily' : 'schema_coordinates_daily'
    } GROUP BY coordinate, hash
  `);

  expect(coordinatesResult.rows).toEqual(2);

  const operationsResult = await clickHouseQuery<{
    target: string;
    client_name: string | null;
    hash: string;
    total: number;
  }>(
    FF_CLICKHOUSE_V2_TABLES
      ? `SELECT hash FROM operation_collection GROUP BY hash`
      : `SELECT hash FROM operations_registry FINAL GROUP BY hash`
  );

  expect(operationsResult.rows).toEqual(1);
});

test('ensure correct data', async () => {
  const { access_token: owner_access_token } = await authenticate('main');
  const orgResult = await createOrganization(
    {
      name: 'foo',
    },
    owner_access_token
  );

  const org = orgResult.body.data!.createOrganization.ok!.createdOrganizationPayload.organization;

  const projectResult = await createProject(
    {
      organization: org.cleanId,
      type: ProjectType.Single,
      name: 'foo',
    },
    owner_access_token
  );

  const project = projectResult.body.data!.createProject.ok!.createdProject;
  const target = projectResult.body.data!.createProject.ok!.createdTargets[0];

  const tokenResult = await createToken(
    {
      name: 'test',
      organization: org.cleanId,
      project: project.cleanId,
      target: target.cleanId,
      organizationScopes: [OrganizationAccessScope.Read],
      projectScopes: [ProjectAccessScope.Read],
      targetScopes: [TargetAccessScope.Read, TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
    },
    owner_access_token
  );

  expect(tokenResult.body.errors).not.toBeDefined();

  const token = tokenResult.body.data!.createToken.ok!.secret;

  await collect({
    operations: [
      {
        operation: 'query ping {        ping      }', // those spaces are expected and important to ensure normalization is in place
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
      },
      {
        operation: 'query ping { ping }',
        operationName: 'ping',
        fields: ['Query', 'Query.ping'],
        execution: {
          ok: true,
          duration: 200000000,
          errorsTotal: 0,
        },
        metadata: {
          client: {
            name: 'test-name',
            version: 'test-version',
          },
        },
      },
    ],
    token,
  });

  await waitFor(5_000);

  if (FF_CLICKHOUSE_V2_TABLES) {
    // operation_collection
    const operationCollectionResult = await clickHouseQuery<{
      target: string;
      hash: string;
      name: string;
      body: string;
      operation_kind: string;
      coordinates: string[];
      total: string;
      timestamp: string;
      expires_at: string;
    }>(`
      SELECT
        target,
        hash,
        name,
        body,
        operation_kind,
        sum(total) as total,
        coordinates
      FROM operation_collection
      GROUP BY target, hash, coordinates, name, body, operation_kind
    `);

    expect(operationCollectionResult.data).toHaveLength(1);

    const operationCollectionRow = operationCollectionResult.data[0];
    expect(operationCollectionRow.body).toEqual('query ping{ping}');
    expect(operationCollectionRow.coordinates).toHaveLength(2);
    expect(operationCollectionRow.coordinates).toContainEqual('Query.ping');
    expect(operationCollectionRow.coordinates).toContainEqual('Query');
    expect(operationCollectionRow.hash).toHaveLength(32);
    expect(operationCollectionRow.name).toBe('ping');
    expect(operationCollectionRow.target).toBe(target.id);
    expect(ensureNumber(operationCollectionRow.total)).toEqual(2);

    // operations
    const operationsResult = await clickHouseQuery<{
      target: string;
      timestamp: string;
      expires_at: string;
      hash: string;
      ok: boolean;
      errors: number;
      duration: number;
      client_name: string;
      client_version: string;
    }>(`
      SELECT
        target,
        timestamp,
        expires_at,
        hash,
        ok,
        errors,
        duration,
        client_name,
        client_version
      FROM operations
    `);

    expect(operationsResult.data).toHaveLength(2);

    const operationWithClient = operationsResult.data.find(o => o.client_name.length > 0)!;
    expect(operationWithClient).toBeDefined();
    expect(operationWithClient.client_name).toEqual('test-name');
    expect(operationWithClient.client_version).toEqual('test-version');
    expect(ensureNumber(operationWithClient.duration)).toEqual(200_000_000);
    expect(ensureNumber(operationWithClient.errors)).toEqual(0);
    expect(operationWithClient.hash).toHaveLength(32);
    expect(operationWithClient.target).toEqual(target.id);

    const operationWithoutClient = operationsResult.data.find(o => o.client_name.length === 0)!;
    expect(operationWithoutClient).toBeDefined();
    expect(operationWithoutClient.client_name).toHaveLength(0);
    expect(operationWithoutClient.client_version).toHaveLength(0);
    expect(ensureNumber(operationWithoutClient.duration)).toEqual(200_000_000);
    expect(ensureNumber(operationWithoutClient.errors)).toEqual(0);
    expect(operationWithoutClient.hash).toHaveLength(32);
    expect(operationWithoutClient.target).toEqual(target.id);

    // operations_hourly
    const operationsHourlyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      total_ok: string;
      total: string;
      quantiles: [number];
    }>(`
      SELECT
        target,
        sum(total) as total,
        sum(total_ok) as total_ok,
        hash,
        quantilesMerge(0.99)(duration_quantiles) as quantiles
      FROM operations_hourly GROUP BY target, hash
    `);

    expect(operationsHourlyResult.data).toHaveLength(1);

    const hourlyAgg = operationsHourlyResult.data[0];
    expect(hourlyAgg).toBeDefined();
    expect(ensureNumber(hourlyAgg.quantiles[0])).toEqual(200_000_000);
    expect(ensureNumber(hourlyAgg.total)).toEqual(2);
    expect(ensureNumber(hourlyAgg.total_ok)).toEqual(2);
    expect(hourlyAgg.hash).toHaveLength(32);
    expect(hourlyAgg.target).toEqual(target.id);

    // operations_daily
    const operationsDailyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      total_ok: string;
      total: string;
      quantiles: [number];
    }>(`
      SELECT
        target,
        sum(total) as total,
        sum(total_ok) as total_ok,
        hash,
        quantilesMerge(0.99)(duration_quantiles) as quantiles
      FROM operations_daily GROUP BY target, hash
    `);

    expect(operationsDailyResult.data).toHaveLength(1);

    const dailyAgg = operationsDailyResult.data[0];
    expect(dailyAgg).toBeDefined();
    expect(ensureNumber(dailyAgg.quantiles[0])).toEqual(200_000_000);
    expect(ensureNumber(dailyAgg.total)).toEqual(2);
    expect(ensureNumber(dailyAgg.total_ok)).toEqual(2);
    expect(dailyAgg.hash).toHaveLength(32);
    expect(dailyAgg.target).toEqual(target.id);

    // coordinates_daily
    const coordinatesDailyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      total: string;
      coordinate: string;
    }>(`
      SELECT
        target,
        sum(total) as total,
        hash,
        coordinate
      FROM coordinates_daily GROUP BY target, hash, coordinate
    `);

    expect(coordinatesDailyResult.data).toHaveLength(2);

    const rootCoordinate = coordinatesDailyResult.data.find(c => c.coordinate === 'Query')!;
    expect(rootCoordinate).toBeDefined();
    expect(ensureNumber(rootCoordinate.total)).toEqual(2);
    expect(rootCoordinate.hash).toHaveLength(32);
    expect(rootCoordinate.target).toEqual(target.id);

    const fieldCoordinate = coordinatesDailyResult.data.find(c => c.coordinate === 'Query.ping')!;
    expect(fieldCoordinate).toBeDefined();
    expect(ensureNumber(fieldCoordinate.total)).toEqual(2);
    expect(fieldCoordinate.hash).toHaveLength(32);
    expect(fieldCoordinate.target).toEqual(target.id);

    // clients_daily
    const clientsDailyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      client_name: string;
      client_version: string;
      total: string;
    }>(`
      SELECT
        target,
        sum(total) as total,
        hash,
        client_name,
        client_version
      FROM clients_daily
      GROUP BY target, hash, client_name, client_version
    `);

    expect(clientsDailyResult.data).toHaveLength(2);

    const dailyAggOfKnownClient = clientsDailyResult.data.find(c => c.client_name === 'test-name')!;
    expect(dailyAggOfKnownClient).toBeDefined();
    expect(ensureNumber(dailyAggOfKnownClient.total)).toEqual(1);
    expect(dailyAggOfKnownClient.client_version).toBe('test-version');
    expect(dailyAggOfKnownClient.hash).toHaveLength(32);
    expect(dailyAggOfKnownClient.target).toEqual(target.id);

    const dailyAggOfUnknownClient = clientsDailyResult.data.find(c => c.client_name !== 'test-name')!;
    expect(dailyAggOfUnknownClient).toBeDefined();
    expect(ensureNumber(dailyAggOfUnknownClient.total)).toEqual(1);
    expect(dailyAggOfUnknownClient.client_version).toHaveLength(0);
    expect(dailyAggOfUnknownClient.hash).toHaveLength(32);
    expect(dailyAggOfUnknownClient.target).toEqual(target.id);
  } else {
    // operations_registry
    const operationsRegistryResult = await clickHouseQuery<{
      target: string;
      hash: string;
      name: string;
      body: string;
      operation: string;
    }>(`
          SELECT
            target,
            hash,
            name,
            body,
            operation
          FROM operations_registry FINAL
          GROUP BY target, hash, name, body, operation
        `);

    expect(operationsRegistryResult.data).toHaveLength(1);

    const operationCollectionRow = operationsRegistryResult.data[0];
    expect(operationCollectionRow.body).toEqual('query ping{ping}');
    expect(operationCollectionRow.hash).toHaveLength(32);
    expect(operationCollectionRow.name).toBe('ping');
    expect(operationCollectionRow.target).toBe(target.id);

    // operations_new
    const operationsResult = await clickHouseQuery<{
      target: string;
      hash: string;
      ok: boolean;
      errors: number;
      duration: number;
      schema: string[];
      client_name: string;
      client_version: string;
    }>(`
          SELECT
            target,
            hash,
            ok,
            errors,
            duration,
            schema,
            client_name,
            client_version
          FROM operations_new
        `);

    expect(operationsResult.data).toHaveLength(2);

    const operationWithClient = operationsResult.data.find(o => o.client_name.length > 0)!;
    expect(operationWithClient).toBeDefined();
    expect(operationWithClient.client_name).toEqual('test-name');
    expect(operationWithClient.client_version).toEqual('test-version');
    expect(operationWithClient.schema).toHaveLength(2);
    expect(operationWithClient.schema).toContainEqual('Query.ping');
    expect(operationWithClient.schema).toContainEqual('Query');
    expect(ensureNumber(operationWithClient.duration)).toEqual(200_000_000);
    expect(ensureNumber(operationWithClient.errors)).toEqual(0);
    expect(operationWithClient.hash).toHaveLength(32);
    expect(operationWithClient.target).toEqual(target.id);

    const operationWithoutClient = operationsResult.data.find(o => o.client_name.length === 0)!;
    expect(operationWithoutClient).toBeDefined();
    expect(operationWithoutClient.client_name).toHaveLength(0);
    expect(operationWithoutClient.client_version).toHaveLength(0);
    expect(operationWithClient.schema).toHaveLength(2);
    expect(operationWithClient.schema).toContainEqual('Query.ping');
    expect(operationWithClient.schema).toContainEqual('Query');
    expect(ensureNumber(operationWithoutClient.duration)).toEqual(200_000_000);
    expect(ensureNumber(operationWithoutClient.errors)).toEqual(0);
    expect(operationWithoutClient.hash).toHaveLength(32);
    expect(operationWithoutClient.target).toEqual(target.id);

    // operations_new_hourly_mv
    const operationsHourlyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      total_ok: string;
      total: string;
      quantiles: [number];
    }>(`
          SELECT
            target,
            hash,
            sum(total) as total,
            sum(total_ok) as total_ok,
            quantilesMerge(0.99)(duration_quantiles) as quantiles
          FROM operations_new_hourly_mv GROUP BY target, hash
        `);

    expect(operationsHourlyResult.data).toHaveLength(1);

    const hourlyAgg = operationsHourlyResult.data[0];
    expect(hourlyAgg).toBeDefined();
    expect(ensureNumber(hourlyAgg.quantiles[0])).toEqual(200_000_000);
    expect(ensureNumber(hourlyAgg.total)).toEqual(2);
    expect(ensureNumber(hourlyAgg.total_ok)).toEqual(2);
    expect(hourlyAgg.hash).toHaveLength(32);
    expect(hourlyAgg.target).toEqual(target.id);

    // schema_coordinates_daily
    const coordinatesDailyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      total: string;
      coordinate: string;
    }>(`
          SELECT
            target,
            sum(total) as total,
            hash,
            coordinate
          FROM schema_coordinates_daily GROUP BY target, hash, coordinate
        `);

    expect(coordinatesDailyResult.data).toHaveLength(2);

    const rootCoordinate = coordinatesDailyResult.data.find(c => c.coordinate === 'Query')!;
    expect(rootCoordinate).toBeDefined();
    expect(ensureNumber(rootCoordinate.total)).toEqual(2);
    expect(rootCoordinate.hash).toHaveLength(32);
    expect(rootCoordinate.target).toEqual(target.id);

    const fieldCoordinate = coordinatesDailyResult.data.find(c => c.coordinate === 'Query.ping')!;
    expect(fieldCoordinate).toBeDefined();
    expect(ensureNumber(fieldCoordinate.total)).toEqual(2);
    expect(fieldCoordinate.hash).toHaveLength(32);
    expect(fieldCoordinate.target).toEqual(target.id);

    // clients_daily
    const clientsDailyResult = await clickHouseQuery<{
      target: string;
      hash: string;
      client_name: string;
      total: string;
    }>(`
          SELECT
            target,
            sum(total) as total,
            hash,
            client_name
          FROM client_names_daily
          GROUP BY target, hash, client_name
        `);

    expect(clientsDailyResult.data).toHaveLength(2);

    const dailyAggOfKnownClient = clientsDailyResult.data.find(c => c.client_name === 'test-name')!;
    expect(dailyAggOfKnownClient).toBeDefined();
    expect(ensureNumber(dailyAggOfKnownClient.total)).toEqual(1);
    expect(dailyAggOfKnownClient.hash).toHaveLength(32);
    expect(dailyAggOfKnownClient.target).toEqual(target.id);

    const dailyAggOfUnknownClient = clientsDailyResult.data.find(c => c.client_name !== 'test-name')!;
    expect(dailyAggOfUnknownClient).toBeDefined();
    expect(ensureNumber(dailyAggOfUnknownClient.total)).toEqual(1);
    expect(dailyAggOfUnknownClient.hash).toHaveLength(32);
    expect(dailyAggOfUnknownClient.target).toEqual(target.id);
  }
});
