import { DataJunctionAPI } from '../DJService';
import { mocks } from '../../../mocks/mockNodes';

describe('DataJunctionAPI', () => {
  let originalEventSource;

  const DJ_URL = 'http://localhost:8000';
  process.env.REACT_APP_DJ_URL = DJ_URL;

  beforeEach(() => {
    fetch.resetMocks();
    originalEventSource = global.EventSource;
    global.EventSource = jest.fn();
  });

  afterEach(() => {
    global.EventSource = originalEventSource;
  });

  it('calls whoami correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.whoami();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/whoami/`, {
      credentials: 'include',
    });
  });

  it('calls logout correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.logout();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/logout/`, {
      credentials: 'include',
      method: 'POST',
    });
  });

  it('calls catalogs correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.catalogs();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/catalogs`, {
      credentials: 'include',
    });
  });

  it('calls engines correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.engines();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/engines`, {
      credentials: 'include',
    });
  });

  it('calls node correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.mockMetricNode));
    const nodeData = await DataJunctionAPI.node(mocks.mockMetricNode.name);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${mocks.mockMetricNode.name}/`,
      {
        credentials: 'include',
      },
    );
    expect(nodeData.name).toEqual(mocks.mockMetricNode.name);
    expect(nodeData.display_name).toEqual(mocks.mockMetricNode.display_name);
    expect(nodeData.type).toEqual(mocks.mockMetricNode.type);
    expect(nodeData.primary_key).toEqual([]);
  });

  it('calls nodeDetails correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify([mocks.mockMetricNode]));
    const nodeData = await DataJunctionAPI.nodeDetails();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/details/`, {
      credentials: 'include',
    });
    expect(nodeData).toEqual([mocks.mockMetricNode]);
  });

  it('calls validate correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify([mocks.mockMetricNode]));
    await DataJunctionAPI.validateNode(
      'metric',
      'default.num_repair_orders',
      'aa',
      'desc',
      'select 1',
    );
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/validate`, {
      credentials: 'include',
      body: JSON.stringify({
        name: 'default.num_repair_orders',
        display_name: 'aa',
        description: 'desc',
        query: 'select 1',
        type: 'metric',
        mode: 'published',
      }),

      headers: {
        'Content-Type': 'application/json',
      },
      method: 'POST',
    });
  });

  it('calls registerTable correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify([mocks.mockMetricNode]));
    await DataJunctionAPI.registerTable('default', 'xyz', 'abc');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/register/table/default/xyz/abc`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'POST',
      },
    );
  });

  it('node with errors are handled', async () => {
    const mockNotFound = { message: 'node not found' };
    fetch.mockResponseOnce(JSON.stringify(mockNotFound));
    const nodeData = await DataJunctionAPI.node(mocks.mockMetricNode.name);
    expect(nodeData.message).toEqual(mockNotFound.message);
  });

  it('calls nodes correctly', async () => {
    const prefix = 'samplePrefix';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.nodes(prefix);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/?prefix=${prefix}`, {
      credentials: 'include',
    });
  });

  it('calls nodesWithType correctly', async () => {
    const nodeType = 'transform';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.nodesWithType(nodeType);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/?node_type=${nodeType}`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls createNode correctly', async () => {
    const sampleArgs = [
      'type',
      'name',
      'display_name',
      'description',
      'query',
      'mode',
      'namespace',
      'primary_key',
    ];
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.createNode(...sampleArgs);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${sampleArgs[0]}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: sampleArgs[1],
        display_name: sampleArgs[2],
        description: sampleArgs[3],
        query: sampleArgs[4],
        mode: sampleArgs[5],
        namespace: sampleArgs[6],
        primary_key: sampleArgs[7],
        metric_metadata: null,
      }),
      credentials: 'include',
    });
  });

  it('calls patchNode correctly', async () => {
    const sampleArgs = [
      'name',
      'display_name',
      'description',
      'query',
      'mode',
      'primary_key',
      'neutral',
      '',
    ];
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.patchNode(...sampleArgs);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${sampleArgs[0]}`, {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        display_name: sampleArgs[1],
        description: sampleArgs[2],
        query: sampleArgs[3],
        mode: sampleArgs[4],
        primary_key: sampleArgs[5],
        metric_metadata: {
          direction: 'neutral',
          unit: '',
          significant_digits: null,
        },
      }),
      credentials: 'include',
    });

    fetch.mockResponseOnce([
      JSON.stringify({ message: 'Update failed' }),
      { status: 200 },
    ]);
    const response = await DataJunctionAPI.patchNode(...sampleArgs);
    expect(response).toEqual({
      json: { message: 'Update failed' },
      status: 500,
    });
  });

  it('calls createCube correctly', async () => {
    const sampleArgs = [
      'default.node_name',
      'Node Display Name',
      'Some readable description',
      'draft',
      ['default.num_repair_orders'],
      [
        'default.date_dim.year',
        'default.date_dim.month',
        'default.date_dim.day',
      ],
      [],
    ];
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.createCube(...sampleArgs);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/cube`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: sampleArgs[0],
        display_name: sampleArgs[1],
        description: sampleArgs[2],
        metrics: sampleArgs[4],
        dimensions: sampleArgs[5],
        filters: sampleArgs[6],
        mode: sampleArgs[3],
      }),
      credentials: 'include',
    });
  });

  it('calls patchCube correctly', async () => {
    const sampleArgs = [
      'default.node_name',
      'Node Display Name',
      'Some readable description',
      'draft',
      ['default.num_repair_orders'],
      [
        'default.date_dim.year',
        'default.date_dim.month',
        'default.date_dim.day',
      ],
      [],
    ];
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.patchCube(...sampleArgs);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/default.node_name`, {
      method: 'PATCH',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        display_name: sampleArgs[1],
        description: sampleArgs[2],
        metrics: sampleArgs[4],
        dimensions: sampleArgs[5],
        filters: sampleArgs[6],
        mode: sampleArgs[3],
      }),
      credentials: 'include',
    });
  });

  it('calls upstreams correctly', async () => {
    const nodeName = 'sampleNode';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.upstreams(nodeName);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/upstream/`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls downstreams correctly', async () => {
    const nodeName = 'sampleNode';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.downstreams(nodeName);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/downstream/`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls node_dag correctly', async () => {
    const nodeName = 'sampleNode';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.node_dag(nodeName);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/dag/`, {
      credentials: 'include',
    });
  });

  it('calls node_lineage correctly', async () => {
    const nodeName = 'sampleNode';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.node_lineage(nodeName);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/lineage/`, {
      credentials: 'include',
    });
  });

  it('calls metric correctly', async () => {
    const metricName = 'sampleMetric';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.metric(metricName);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/metrics/${metricName}/`, {
      credentials: 'include',
    });
  });

  it('calls metrics correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.metrics('');
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/metrics/`, {
      credentials: 'include',
    });
  });

  it('calls listMetricMetadata correctly', async () => {
    const nodeType = 'transform';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.listMetricMetadata();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/metrics/metadata`, {
      method: 'GET',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
    });
  });

  it('calls namespaces correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.namespaces();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/`, {
      credentials: 'include',
    });
  });

  it('calls clientCode correctly', async () => {
    const name = 'sampleName';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.clientCode(name);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/datajunction-clients/python/new_node/${name}`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls cube correctly', async () => {
    const name = 'sampleCube';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.cube(name);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/cubes/${name}/`, {
      credentials: 'include',
    });
  });

  it('calls commonDimensions correctly', async () => {
    const metrics = ['metric1', 'metric2'];
    const query = metrics.map(m => `metric=${m}`).join('&');
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.commonDimensions(metrics);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/metrics/common/dimensions/?${query}`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls history correctly', async () => {
    const type = 'sampleType';
    const name = 'sampleName';
    const offset = 10;
    const limit = 100;
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.history(type, name, offset, limit);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/history?node=${name}&offset=${offset}&limit=${limit}`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls revisions correctly', async () => {
    const name = 'sampleName';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.revisions(name);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${name}/revisions/`, {
      credentials: 'include',
    });
  });

  it('calls namespace correctly', async () => {
    const nmspce = 'sampleNamespace';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.namespace(nmspce);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/namespaces/${nmspce}?edited_by=undefined&with_edited_by=true`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls sql correctly', async () => {
    const metric_name = 'sampleMetric';
    const selection = { key: 'value' };
    const params = new URLSearchParams(selection).toString();
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.sql(metric_name, selection);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/sql/${metric_name}?${params}`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls nodesWithDimension correctly', async () => {
    const name = 'sampleName';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.nodesWithDimension(name);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/dimensions/${name}/nodes/`, {
      credentials: 'include',
    });
  });

  it('calls materializations correctly', async () => {
    const nodeName = 'default.sample_node';
    const mockMaterializations = [
      { name: 'materialization1' },
      { name: 'materialization2' },
    ];

    // Mock the first fetch call to return the list of materializations
    fetch.mockResponseOnce(JSON.stringify(mockMaterializations));

    // Mock the subsequent fetch calls to return clientCode for each materialization
    fetch.mockResponses(
      ...mockMaterializations.map(mat => [
        JSON.stringify('from dj import DJClient'),
      ]),
    );

    const result = await DataJunctionAPI.materializations(nodeName);

    // Check the first fetch call
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/materializations/`,
      {
        credentials: 'include',
      },
    );

    // Ensure the result contains the clientCode for each materialization
    expect(result).toEqual(mockMaterializations);
  });

  it('calls columns correctly', async () => {
    const sampleNode = {
      name: 'sampleNode',
      columns: [
        { name: 'column1', dimension: { name: 'dimension1' } },
        { name: 'column2', dimension: null },
        { name: 'column3', dimension: { name: 'dimension2' } },
      ],
    };

    // Mock the fetch calls to return clientCode for columns with a dimension
    const mockClientCodeResponses = sampleNode.columns
      .filter(col => col.dimension)
      .map(col => [JSON.stringify({ clientCode: col.clientCode })]);

    fetch.mockResponses(...mockClientCodeResponses);

    const result = await DataJunctionAPI.columns(sampleNode);
    expect(result).toEqual(sampleNode.columns);
  });

  it('calls sqls correctly', async () => {
    const metricSelection = ['metric1'];
    const dimensionSelection = ['dimension1'];
    const filters = 'sampleFilter';
    const params = new URLSearchParams();
    metricSelection.forEach(metric => params.append('metrics', metric));
    dimensionSelection.forEach(dimension =>
      params.append('dimensions', dimension),
    );
    params.append('filters', filters);
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.sqls(metricSelection, dimensionSelection, filters);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/sql/?${params}`, {
      credentials: 'include',
    });
  });

  it('calls data correctly', async () => {
    const metricSelection = ['metric1'];
    const dimensionSelection = ['dimension1'];
    const params = new URLSearchParams();
    metricSelection.forEach(metric => params.append('metrics', metric));
    dimensionSelection.forEach(dimension =>
      params.append('dimensions', dimension),
    );
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.data(metricSelection, dimensionSelection);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/data/?${params}&limit=10000`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls compiledSql correctly', async () => {
    const node = 'sampleNode';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.compiledSql(node);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/sql/${node}/`, {
      credentials: 'include',
    });
  });

  it('transforms node data correctly', async () => {
    fetch.mockResolvedValueOnce({
      json: () =>
        Promise.resolve({
          message: undefined,
          columns: [
            {
              name: 'id',
              attributes: [
                {
                  attribute_type: {
                    name: 'primary_key',
                  },
                },
              ],
            },
          ],
        }),
      status: 200,
    });

    const nodeData = await DataJunctionAPI.node('sampleNodeName');
    expect(nodeData.primary_key).toEqual(['id']);
  });

  it('calls stream correctly', () => {
    const metricSelection = ['metric1', 'metric2'];
    const dimensionSelection = ['dimension1', 'dimension2'];
    const filters = 'sampleFilter';

    DataJunctionAPI.stream(metricSelection, dimensionSelection, filters);

    const params = new URLSearchParams();
    metricSelection.map(metric => params.append('metrics', metric));
    dimensionSelection.map(dimension => params.append('dimensions', dimension));
    params.append('filters', filters);

    expect(global.EventSource).toHaveBeenCalledWith(
      `${DJ_URL}/stream/?${params}&limit=10000&async_=true`,
      { withCredentials: true },
    );
  });

  it('calls streamNodeData correctly', () => {
    const nodes = ['transform1'];
    const dimensionSelection = ['dimension1', 'dimension2'];
    const filters = 'sampleFilter';

    DataJunctionAPI.streamNodeData(nodes[0], {
      dimensions: dimensionSelection,
      filters: filters,
    });

    const params = new URLSearchParams();
    params.append('dimensions', dimensionSelection, 'filters', filters);

    expect(global.EventSource).toHaveBeenCalledWith(
      `${DJ_URL}/stream/transform1?filters=sampleFilter&dimensions=dimension1&dimensions=dimension2&limit=1000&async_=true`,
      { withCredentials: true },
    );
  });

  it('calls nodeData correctly', () => {
    const nodes = ['transform1'];
    const dimensionSelection = ['dimension1', 'dimension2'];
    const filters = 'sampleFilter';
    fetch.mockResponseOnce(JSON.stringify({}));

    DataJunctionAPI.nodeData(nodes[0], {
      dimensions: dimensionSelection,
      filters: filters,
    });

    const params = new URLSearchParams();
    params.append('dimensions', dimensionSelection, 'filters', filters);

    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/data/transform1?filters=sampleFilter&dimensions=dimension1&dimensions=dimension2&limit=1000&async_=true`,
      {
        credentials: 'include',
        headers: {
          'Cache-Control': 'max-age=86400',
        },
      },
    );
  });

  it('calls dag correctly and processes response', async () => {
    const mockResponse = [
      {
        name: 'namespace.node1',
        parents: [{ name: 'parent1' }, { name: null }],
        columns: [
          {
            dimension: { name: 'dimension1' },
            name: 'col1',
            type: 'type1',
            attributes: [],
          },
          {
            dimension: null,
            name: 'col2',
            type: 'type2',
            attributes: [{ attribute_type: { name: 'primary_key' } }],
          },
        ],
        table: 'table1',
        schema_: 'schema1',
        display_name: 'DisplayName1',
        type: 'type1',
      },
    ];

    fetch.mockResponseOnce(JSON.stringify(mockResponse));
    const result = await DataJunctionAPI.dag();

    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/`, {
      credentials: 'include',
    });

    expect(result.edges).toEqual([
      {
        id: 'namespace.node1-parent1',
        target: 'namespace.node1',
        source: 'parent1',
        animated: true,
        markerEnd: { type: 'arrow' },
      },
      {
        id: 'namespace.node1-dimension1',
        target: 'namespace.node1',
        source: 'dimension1',
        draggable: true,
      },
    ]);

    expect(result.nodes).toEqual([
      {
        id: 'namespace.node1',
        type: 'DJNode',
        data: {
          label: 'schema1.table1',
          table: 'table1',
          name: 'namespace.node1',
          display_name: 'DisplayName1',
          type: 'type1',
          primary_key: ['col2'],
          column_names: [
            { name: 'col1', type: 'type1' },
            { name: 'col2', type: 'type2' },
          ],
        },
      },
    ]);

    expect(result.namespaces).toEqual([
      {
        id: 'namespace',
        type: 'DJNamespace',
        data: {
          label: 'namespace',
        },
      },
    ]);
  });

  it('calls link and unlink dimension correctly', async () => {
    const nodeName = 'default.transform1';
    const dimensionName = 'default.dimension1';
    const columnName = 'column1';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.unlinkDimension(nodeName, columnName, dimensionName);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'DELETE',
      },
    );

    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.linkDimension(nodeName, columnName, dimensionName);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/columns/${columnName}?dimension=${dimensionName}`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'POST',
      },
    );
  });

  it('calls add and remove complex dimension link correctly', async () => {
    const nodeName = 'default.transform1';
    const dimensionNode = 'default.dimension1';
    const joinOn = 'blah';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.removeComplexDimensionLink(nodeName, dimensionNode);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/link`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        dimensionNode: dimensionNode,
        role: null,
      }),
      method: 'DELETE',
    });

    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.addComplexDimensionLink(
      nodeName,
      dimensionNode,
      joinOn,
    );
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}/link`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        dimensionNode: dimensionNode,
        joinType: null,
        joinOn: joinOn,
        joinCardinality: null,
        role: null,
      }),
      method: 'POST',
    });
  });

  it('calls deactivate correctly', async () => {
    const nodeName = 'default.transform1';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.deactivate(nodeName);
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/nodes/${nodeName}`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'DELETE',
    });
  });

  it('calls attributes correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.attributes));
    await DataJunctionAPI.attributes();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/attributes`, {
      credentials: 'include',
    });
  });

  it('calls dimensions correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.dimensions));
    await DataJunctionAPI.dimensions();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/dimensions`, {
      credentials: 'include',
    });
  });

  it('calls setAttributes correctly', async () => {
    const nodeName = 'default.transform1';
    const attributes = ['system'];
    const columnName = 'column1';
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.setAttributes(nodeName, columnName, attributes);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/columns/${columnName}/attributes`,
      {
        credentials: 'include',
        body: JSON.stringify([{ namespace: 'system', name: 'system' }]),
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'POST',
      },
    );
  });

  it('calls addNamespace correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.addNamespace('test');
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/test`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'POST',
    });
  });

  it('calls listTags correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.tags));
    const res = await DataJunctionAPI.listTags();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/tags`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'GET',
    });
    expect(res).toEqual(mocks.tags);
  });

  it('calls getTag correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.tags[0]));
    const res = await DataJunctionAPI.getTag('report.financials');
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/tags/report.financials`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      method: 'GET',
    });
    expect(res).toEqual(mocks.tags[0]);
  });

  it('calls listNodesForTag correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.mockLinkedNodes));
    const res = await DataJunctionAPI.listNodesForTag('report.financials');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/tags/report.financials/nodes`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'GET',
      },
    );
    expect(res).toEqual(mocks.mockLinkedNodes);
  });

  it('calls tagsNode correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.mockLinkedNodes));
    await DataJunctionAPI.tagsNode('default.num_repair_orders', [
      'report.financials',
      'report.forecasts',
    ]);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/default.num_repair_orders/tags?tag_names=report.financials&tag_names=report.forecasts`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        method: 'POST',
      },
    );
  });

  it('calls addTag correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify(mocks.mockLinkedNodes));
    await DataJunctionAPI.addTag(
      'report.financials',
      'Financial Reports',
      'report',
      'financial reports',
    );
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/tags`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: 'report.financials',
        display_name: 'Financial Reports',
        tag_type: 'report',
        description: 'financial reports',
      }),
      method: 'POST',
    });
  });

  it('calls editTag correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.editTag(
      'report.financials',
      'Financial reports',
      'Financial Reports',
    );
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/tags/report.financials`, {
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        description: 'Financial reports',
        display_name: 'Financial Reports',
      }),
      method: 'PATCH',
    });
  });

  it('calls setPartition correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.setPartition(
      'default.hard_hat',
      'hire_date',
      'temporal',
      'yyyyMMdd',
      'day',
    );
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/default.hard_hat/columns/hire_date/partition`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          type_: 'temporal',
          format: 'yyyyMMdd',
          granularity: 'day',
        }),
        method: 'POST',
      },
    );
  });

  it('calls materialize correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.materialize(
      'default.hard_hat',
      'spark_sql',
      'full',
      '@daily',
      {},
    );
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/default.hard_hat/materialization`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          job: 'spark_sql',
          strategy: 'full',
          schedule: '@daily',
          config: {},
        }),
        method: 'POST',
      },
    );
  });

  it('calls runBackfill correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.runBackfill('default.hard_hat', 'spark', [
      {
        columnName: 'hire_date',
        range: ['20230101', '20230202'],
      },
    ]);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/default.hard_hat/materializations/spark/backfill`,
      {
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify([
          {
            column_name: 'hire_date',
            range: ['20230101', '20230202'],
          },
        ]),
        method: 'POST',
      },
    );
  });

  it('calls materializationInfo correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.materializationInfo();
    expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/materialization/info`, {
      credentials: 'include',
    });
  });

  it('calls revalidate correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.revalidate('default.hard_hat');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/default.hard_hat/validate`,
      {
        credentials: 'include',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );
  });

  it('calls deleteMaterialization correctly', () => {
    const nodeName = 'transform1';
    const materializationName = 'sampleMaterialization';
    fetch.mockResponseOnce(JSON.stringify({}));

    DataJunctionAPI.deleteMaterialization(nodeName, materializationName);
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/nodes/${nodeName}/materializations?materialization_name=${materializationName}`,
      {
        method: 'DELETE',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );
  });

  it('calls listNodesForLanding correctly', () => {
    fetch.mockResponseOnce(JSON.stringify({}));

    DataJunctionAPI.listNodesForLanding(
      '',
      ['source'],
      [],
      '',
      null,
      null,
      100,
    );
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/graphql`,
      expect.objectContaining({
        method: 'POST',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
      }),
    );
  });

  it('calls getMetric correctly', async () => {
    fetch.mockResponseOnce(
      JSON.stringify({
        data: { findNodes: [{ name: 'default.num_repair_orders' }] },
      }),
    );
    await DataJunctionAPI.getMetric('default.num_repair_orders');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/graphql`,
      expect.objectContaining({
        method: 'POST',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
        },
      }),
    );
  });

  it('calls notebookExportCube correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.notebookExportCube('default.repairs_cube');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/datajunction-clients/python/notebook/?cube=default.repairs_cube`,
      {
        credentials: 'include',
      },
    );
  });

  it('calls notebookExportNamespace correctly', async () => {
    fetch.mockResponseOnce(JSON.stringify({}));
    await DataJunctionAPI.notebookExportNamespace('default');
    expect(fetch).toHaveBeenCalledWith(
      `${DJ_URL}/datajunction-clients/python/notebook/?namespace=default`,
      {
        credentials: 'include',
      },
    );
  });
});
