/*
 * Copyright 2020 Spotify AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { Lockfile } from './Lockfile';

const HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

`;

const mockA = `${HEADER}
a@^1:
  version "1.0.1"
  resolved "https://my-registry/a-1.0.01.tgz#abc123"
  integrity sha512-xyz
  dependencies:
    b "^2"

b@2.0.x:
  version "2.0.1"

b@^2:
  version "2.0.0"
`;

const mockADedup = `${HEADER}
a@^1:
  version "1.0.1"
  resolved "https://my-registry/a-1.0.01.tgz#abc123"
  integrity sha512-xyz
  dependencies:
    b "^2"

b@2.0.x, b@^2:
  version "2.0.1"
`;

const mockB = `${HEADER}
"@s/a@*", "@s/a@1 || 2", "@s/a@^1":
  version "1.0.1"

"@s/a@^2.0.x":
  version "2.0.0"
`;

const mockBDedup = `${HEADER}
"@s/a@*", "@s/a@1 || 2", "@s/a@^2.0.x":
  version "2.0.0"

"@s/a@^1":
  version "1.0.1"
`;

describe('Lockfile', () => {
  afterEach(() => {
    mockFs.restore();
  });

  it('should load and serialize mockA', async () => {
    mockFs({
      '/yarn.lock': mockA,
    });

    const lockfile = await Lockfile.load('/yarn.lock');
    expect(lockfile.get('a')).toEqual([{ range: '^1', version: '1.0.1' }]);
    expect(lockfile.get('b')).toEqual([
      { range: '2.0.x', version: '2.0.1' },
      { range: '^2', version: '2.0.0' },
    ]);
    expect(lockfile.toString()).toBe(mockA);
  });

  it('should deduplicate and save mockA', async () => {
    mockFs({
      '/yarn.lock': mockA,
    });

    const lockfile = await Lockfile.load('/yarn.lock');
    const result = lockfile.analyze();
    expect(result).toEqual({
      invalidRanges: [],
      newRanges: [],
      newVersions: [
        {
          name: 'b',
          range: '^2',
          oldVersion: '2.0.0',
          newVersion: '2.0.1',
        },
      ],
    });

    expect(lockfile.toString()).toBe(mockA);
    lockfile.replaceVersions(result.newVersions);
    expect(lockfile.toString()).toBe(mockADedup);

    await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(mockA);
    await expect(lockfile.save()).resolves.toBeUndefined();
    await expect(fs.readFile('/yarn.lock', 'utf8')).resolves.toBe(mockADedup);
  });

  it('should deduplicate mockB', async () => {
    mockFs({
      '/yarn.lock': mockB,
    });

    const lockfile = await Lockfile.load('/yarn.lock');
    const result = lockfile.analyze();
    expect(result).toEqual({
      invalidRanges: [],
      newRanges: [
        {
          name: '@s/a',
          oldRange: '^1',
          newRange: '^2.0.x',
          oldVersion: '1.0.1',
          newVersion: '2.0.0',
        },
      ],
      newVersions: [
        {
          name: '@s/a',
          range: '*',
          oldVersion: '1.0.1',
          newVersion: '2.0.0',
        },
        {
          name: '@s/a',
          range: '1 || 2',
          oldVersion: '1.0.1',
          newVersion: '2.0.0',
        },
      ],
    });

    expect(lockfile.toString()).toBe(mockB);
    lockfile.replaceVersions(result.newVersions);
    expect(lockfile.toString()).toBe(mockBDedup);
  });
});
