async def test_skip_loaded_versions(self, fake_future):
        workspace = MagicMock()
        workspace.list_versions.return_value = fake_future(
            ["1.0", "10.1", "2.0"])

        hasher = RepositoryHasher(storage=MagicMock(), hasher=MagicMock())
        hasher.collect_for_version = MagicMock()
        hasher.collect_for_version.return_value = fake_future([
            Signature(path="wp-content/plugins/a-plugin/readme.txt",
                      hash="12345")
        ])

        stored = VersionList(producer="RepositoryHasher",
                             key="plugins/a-plugin")
        stored.get_version("1.0", create_missing=True)
        stored.get_version("2.0", create_missing=True)

        hasher.storage.read_versions.return_value = stored

        await hasher.collect_for_workspace(
            "plugins/a-plugin",
            workspace,
            prefix="wp-content/plugins/a-plugin")

        hasher.storage.read_versions.assert_called_with("plugins/a-plugin")
        hasher.collect_for_version.assert_called_once_with(
            workspace, "10.1", prefix="wp-content/plugins/a-plugin")
    async def test_collect_from_meta_for_plugin(self, fake_future):
        workspace = MagicMock()
        workspace.prepare.return_value = fake_future(None)

        @contextmanager
        def workspace_provider(repository):
            self.assertEqual(repository, "https://svn.example.com/a-plugin")
            yield workspace

        subversion = MagicMock()
        subversion.workspace = workspace_provider

        meta = Meta(key="plugins/a-plugin",
                    name="A Plugin",
                    repositories=[
                        Repository(
                            type="subversion",
                            location="https://svn.example.com/a-plugin"),
                    ])
        hasher = RepositoryHasher(storage=MagicMock(),
                                  hasher=MagicMock(),
                                  subversion=subversion)
        hasher.collect_for_workspace = MagicMock()
        hasher.collect_for_workspace.return_value = fake_future(None)
        self.assertTrue(await hasher.collect_from_meta(
            meta, prefix_pattern="wp-content/{meta.key}"))

        workspace.prepare.assert_called_once_with()
        hasher.collect_for_workspace.assert_called_with(
            "plugins/a-plugin",
            workspace,
            prefix="wp-content/plugins/a-plugin")
    async def test_with_multiple_repos(self, fake_future):
        workspace = MagicMock()
        workspace.prepare.return_value = fake_future(None)

        @contextmanager
        def workspace_provider(repository):
            self.assertEqual(repository, "https://svn.example.com")
            yield workspace

        subversion = MagicMock()
        subversion.workspace = workspace_provider

        meta = Meta(key="wordpress",
                    name="WordPress",
                    repositories=[
                        Repository(type="cvs", location="1990s"),
                        Repository(type="subversion",
                                   location="https://svn.example.com"),
                        Repository(type="subversion",
                                   location="https://mirror.example.com"),
                    ])
        hasher = RepositoryHasher(storage=MagicMock(),
                                  hasher=MagicMock(),
                                  subversion=subversion)
        hasher.collect_for_workspace = MagicMock()
        hasher.collect_for_workspace.return_value = fake_future(None)
        self.assertTrue(await hasher.collect_from_meta(meta))

        workspace.prepare.assert_called_once_with()
    async def test_collect_for_one_version(self, fake_future):
        with patch("openwebvulndb.common.hash.HashCollector") as HashCollector:
            workspace = MagicMock()
            workspace.workdir = "/my/workspace/path"
            workspace.to_version.return_value = fake_future(None)

            collector = MagicMock()
            collector.collect.return_value = ["Hello"]
            HashCollector.return_value = collector

            hasher = RepositoryHasher(storage=MagicMock(),
                                      hasher=Hasher("SHA256"))
            out = await hasher.collect_for_version(workspace,
                                                   "2.1",
                                                   prefix="test-prefix")

            self.assertEqual(["Hello"], out)

            HashCollector.assert_called_with(path="/my/workspace/path",
                                             hasher=hasher.hasher,
                                             prefix="test-prefix",
                                             lookup_version="2.1")

            collector.collect.assert_called_with()

            workspace.to_version.assert_called_with("2.1")
    async def test_collect_for_workspace_do_not_write_versions_if_empty_signatures(
            self):
        hasher = RepositoryHasher(storage=MagicMock(), hasher=Hasher("SHA256"))
        hasher.collect_for_version = make_mocked_coro(return_value=[])
        hasher.get_version_list = MagicMock(return_value=VersionList(
            key="plugins/plugin-key",
            producer="unittest",
            versions=[VersionDefinition(version="1.0")]))
        workspace = MagicMock()
        workspace.list_versions = make_mocked_coro(return_value=["1.2"])
        hasher.storage.write_versions = MagicMock()

        await hasher.collect_for_workspace("plugins/plugin-key", workspace)

        hasher.collect_for_version.assert_called_once_with(workspace,
                                                           "1.2",
                                                           prefix="")
        hasher.storage.write_versions.assert_not_called()
    async def test_brand_new_file(self, fake_future):
        workspace = MagicMock()
        workspace.list_versions.return_value = fake_future(
            ["1.0", "10.1", "2.0"])

        hasher = RepositoryHasher(storage=MagicMock(), hasher=MagicMock())
        hasher.collect_for_version = MagicMock()
        hasher.collect_for_version.return_value = fake_future([
            Signature(path="wp-content/plugins/a-plugin/readme.txt",
                      hash="12345")
        ])

        hasher.storage.read_versions.side_effect = FileNotFoundError()

        await hasher.collect_for_workspace(
            "plugins/a-plugin",
            workspace,
            prefix="wp-content/plugins/a-plugin")

        hasher.storage.read_versions.assert_called_with("plugins/a-plugin")
        hasher.collect_for_version.assert_has_calls([
            call(workspace, "1.0", prefix="wp-content/plugins/a-plugin"),
            call(workspace, "2.0", prefix="wp-content/plugins/a-plugin"),
            call(workspace, "10.1", prefix="wp-content/plugins/a-plugin"),
        ],
                                                    any_order=False)

        expect = VersionList(producer="RepositoryHasher",
                             key="plugins/a-plugin")
        v1 = expect.get_version("1.0", create_missing=True)
        v1.add_signature("wp-content/plugins/a-plugin/readme.txt",
                         hash="12345")
        v2 = expect.get_version("2.0", create_missing=True)
        v2.add_signature("wp-content/plugins/a-plugin/readme.txt",
                         hash="12345")
        v10 = expect.get_version("10.1", create_missing=True)
        v10.add_signature("wp-content/plugins/a-plugin/readme.txt",
                          hash="12345")

        hasher.storage.write_versions.assert_called_with(expect)
    async def test_skip_bad_branches(self, fake_future):
        workspace = MagicMock()
        workspace.list_versions.return_value = fake_future(
            ["foobar.zip", "1.0"])

        hasher = RepositoryHasher(storage=MagicMock(), hasher=MagicMock())
        hasher.collect_for_version = MagicMock()
        hasher.collect_for_version.side_effect = DirectoryExpected()

        hasher.storage.read_versions.side_effect = FileNotFoundError()

        await hasher.collect_for_workspace(
            "plugins/a-plugin",
            workspace,
            prefix="wp-content/plugins/a-plugin")

        hasher.storage.read_versions.assert_called_with("plugins/a-plugin")
        hasher.collect_for_version.assert_has_calls([
            call(workspace, "foobar.zip",
                 prefix="wp-content/plugins/a-plugin"),
            call(workspace, "1.0", prefix="wp-content/plugins/a-plugin"),
        ],
                                                    any_order=False)
    async def test_execution_failures(self, fake_future):
        workspace = MagicMock()
        workspace.list_versions.return_value = fake_future(
            ["1.0", "10.1", "2.0"])

        hasher = RepositoryHasher(storage=MagicMock(), hasher=MagicMock())
        hasher.collect_for_version = MagicMock()
        hasher.collect_for_version.side_effect = ExecutionFailure()

        hasher.storage.read_versions.side_effect = FileNotFoundError()

        await hasher.collect_for_workspace(
            "plugins/a-plugin",
            workspace,
            prefix="wp-content/plugins/a-plugin")

        hasher.storage.read_versions.assert_called_with("plugins/a-plugin")
        hasher.collect_for_version.assert_has_calls([
            call(workspace, "1.0", prefix="wp-content/plugins/a-plugin"),
        ],
                                                    any_order=False)

        hasher.storage.write_versions.assert_not_called()