Example #1
0
def test_list_resource_revisions_ok(client_mock, config):
    """One resource revision ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {
        "revisions": [
            {
                "created-at": "2021-02-11T13:43:22.396606",
                "name": "otherstuff",
                "revision": 1,
                "sha256": "1bf0399c2de1240777ba73785f1ff1de5331f12853765a0",
                "sha3-384": "deb9369cb2b9e86ad44160e93da43d240e6388c5dc67b8e2a5a3c2a36a26fe4c89",
                "sha384": "eaaba6aa119da415e6ad778358a8530c47fefbe3ceced258e8c25530107dc7908e",
                "sha512": (
                    "b8cfe885d49285d8546885167a72fd56ea23480e17c9cdd8e06b45239d79b774c6d6fc09d"
                ),
                "size": 500,
            },
        ]
    }

    result = store.list_resource_revisions("charm-name", "resource-name")

    assert client_mock.mock_calls == [
        call.get("/v1/charm/charm-name/resources/resource-name/revisions")
    ]

    (item,) = result
    assert item.revision == 1
    assert item.created_at == parser.parse("2021-02-11T13:43:22.396606")
    assert item.size == 500
Example #2
0
def test_list_resources_ok(client_mock, config):
    """One resource ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {
        "resources": [
            {
                "name": "testresource",
                "optional": True,
                "revision": 9,
                "type": "file",
            },
        ]
    }

    result = store.list_resources("some-name")

    assert client_mock.mock_calls == [
        call.get("/v1/charm/some-name/resources")
    ]

    (item, ) = result
    assert item.name == "testresource"
    assert item.optional
    assert item.revision == 9
    assert item.resource_type == "file"
Example #3
0
def test_list_resources_ok(client_mock, config):
    """One resource ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {
        'resources': [
            {
                'name': 'testresource',
                'optional': True,
                'revision': 9,
                'type': 'file',
            },
        ]
    }

    result = store.list_resources('some-name')

    assert client_mock.mock_calls == [
        call.get('/v1/charm/some-name/resources')
    ]

    (item, ) = result
    assert item.name == 'testresource'
    assert item.optional
    assert item.revision == 9
    assert item.resource_type == 'file'
Example #4
0
def test_list_revisions_errors(client_mock, config):
    """One revision with errors."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {
        "revisions": [
            {
                "revision": 7,
                "version": "v7",
                "created-at": "2020-06-29T22:11:00.123",
                "status": "rejected",
                "errors": [
                    {"message": "error text 1", "code": "error-code-1"},
                    {"message": "error text 2", "code": "error-code-2"},
                ],
            }
        ]
    }

    result = store.list_revisions("some-name")

    assert client_mock.mock_calls == [call.get("/v1/charm/some-name/revisions")]

    (item,) = result
    error1, error2 = item.errors
    assert error1.message == "error text 1"
    assert error1.code == "error-code-1"
    assert error2.message == "error text 2"
    assert error2.code == "error-code-2"
Example #5
0
def test_list_revisions_errors(client_mock, config):
    """One revision with errors."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {'revisions': [
        {
            'revision': 7,
            'version': 'v7',
            'created-at': '2020-06-29T22:11:00.123',
            'status': 'rejected',
            'errors': [
                {'message': "error text 1", 'code': "error-code-1"},
                {'message': "error text 2", 'code': "error-code-2"},
            ],
        }
    ]}

    result = store.list_revisions('some-name')

    assert client_mock.mock_calls == [
        call.get('/v1/charm/some-name/revisions')
    ]

    (item,) = result
    error1, error2 = item.errors
    assert error1.message == "error text 1"
    assert error1.code == "error-code-1"
    assert error2.message == "error text 2"
    assert error2.code == "error-code-2"
Example #6
0
def test_list_registered_names_multiple(client_mock, config):
    """List registered names getting a multiple response."""
    store = Store(config.charmhub)

    auth_response = {
        "results": [
            {
                "name": "name1",
                "type": "charm",
                "private": False,
                "status": "status1"
            },
            {
                "name": "name2",
                "type": "bundle",
                "private": True,
                "status": "status2"
            },
        ]
    }
    client_mock.get.return_value = auth_response

    result = store.list_registered_names()

    assert client_mock.mock_calls == [call.get("/v1/charm")]
    item1, item2 = result
    assert item1.name == "name1"
    assert item1.entity_type == "charm"
    assert not item1.private
    assert item1.status == "status1"
    assert item2.name == "name2"
    assert item2.entity_type == "bundle"
    assert item2.private
    assert item2.status == "status2"
Example #7
0
def test_release_multiple_channels(client_mock, config):
    """Releasing a revision into multiple channels."""
    store = Store(config.charmhub)
    store.release('testname', 123, ['channel1', 'channel2', 'channel3'], [])

    expected_body = [
        {
            'revision': 123,
            'channel': 'channel1',
            'resources': []
        },
        {
            'revision': 123,
            'channel': 'channel2',
            'resources': []
        },
        {
            'revision': 123,
            'channel': 'channel3',
            'resources': []
        },
    ]
    assert client_mock.mock_calls == [
        call.post('/v1/charm/testname/releases', expected_body),
    ]
Example #8
0
def test_get_library(client_mock, config):
    """Get all the information (including content) for a library revision."""
    test_charm_name = "test-charm-name"
    test_lib_name = "test-lib-name"
    test_lib_id = "test-lib-id"
    test_api = "test-api-version"
    test_patch = "test-patch-version"
    test_content = "test content with quite a lot of funny Python code :p"
    test_hash = "1234"

    store = Store(config.charmhub)
    client_mock.get.return_value = {
        "api": test_api,
        "content": test_content,
        "hash": test_hash,
        "library-id": test_lib_id,
        "library-name": test_lib_name,
        "charm-name": test_charm_name,
        "patch": test_patch,
    }

    result_lib = store.get_library(test_charm_name, test_lib_id, test_api)

    assert client_mock.mock_calls == [
        call.get("/v1/charm/libraries/test-charm-name/{}?api={}".format(
            test_lib_id, test_api)),
    ]
    assert result_lib.api == test_api
    assert result_lib.content == test_content
    assert result_lib.content_hash == test_hash
    assert result_lib.lib_id == test_lib_id
    assert result_lib.lib_name == test_lib_name
    assert result_lib.charm_name == test_charm_name
    assert result_lib.patch == test_patch
Example #9
0
def test_get_tips_query_combinations(client_mock, config):
    """Use all the combinations to specify what's queried."""
    store = Store(config.charmhub)
    client_mock.post.return_value = {"libraries": []}

    query_info = [
        {
            "lib_id": "test-lib-id-1"
        },
        {
            "lib_id": "test-lib-id-2",
            "api": 2
        },
        {
            "charm_name": "test-charm-name-3"
        },
        {
            "charm_name": "test-charm-name-4",
            "api": 4
        },
        {
            "charm_name": "test-charm-name-5",
            "lib_name": "test-lib-name-5"
        },
        {
            "charm_name": "test-charm-name-6",
            "lib_name": "test-lib-name-6",
            "api": 6
        },
    ]
    store.get_libraries_tips(query_info)

    payload = [
        {
            "library-id": "test-lib-id-1"
        },
        {
            "library-id": "test-lib-id-2",
            "api": 2
        },
        {
            "charm-name": "test-charm-name-3"
        },
        {
            "charm-name": "test-charm-name-4",
            "api": 4
        },
        {
            "charm-name": "test-charm-name-5",
            "library-name": "test-lib-name-5"
        },
        {
            "charm-name": "test-charm-name-6",
            "library-name": "test-lib-name-6",
            "api": 6,
        },
    ]
    assert client_mock.mock_calls == [
        call.post("/v1/charm/libraries/bulk", payload),
    ]
Example #10
0
def test_release_with_resources(client_mock, config):
    """Releasing with resources attached."""
    store = Store(config.charmhub)
    r1 = ResourceOption(name='foo', revision=3)
    r2 = ResourceOption(name='bar', revision=17)
    store.release('testname', 123, ['channel1', 'channel2'], [r1, r2])

    expected_body = [
        {
            'revision': 123,
            'channel': 'channel1',
            'resources': [
                {'name': 'foo', 'revision': 3},
                {'name': 'bar', 'revision': 17},
            ]
        }, {
            'revision': 123,
            'channel': 'channel2',
            'resources': [
                {'name': 'foo', 'revision': 3},
                {'name': 'bar', 'revision': 17},
            ]
        }
    ]
    assert client_mock.mock_calls == [
        call.post('/v1/charm/testname/releases', expected_body),
    ]
Example #11
0
def test_list_revisions_ok(client_mock, config):
    """One revision ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {'revisions': [
        {
            'revision': 7,
            'version': 'v7',
            'created-at': '2020-06-29T22:11:00.123',
            'status': 'approved',
            'errors': None,
        }
    ]}

    result = store.list_revisions('some-name')

    assert client_mock.mock_calls == [
        call.get('/v1/charm/some-name/revisions')
    ]

    (item,) = result
    assert item.revision == 7
    assert item.version == 'v7'
    assert item.created_at == parser.parse('2020-06-29T22:11:00.123')
    assert item.status == 'approved'
    assert item.errors == []
Example #12
0
def test_list_registered_names_multiple(client_mock):
    """List registered names getting a multiple response."""
    store = Store()

    auth_response = {
        'results': [
            {
                'name': 'name1',
                'private': False,
                'status': 'status1'
            },
            {
                'name': 'name2',
                'private': True,
                'status': 'status2'
            },
        ]
    }
    client_mock.get.return_value = auth_response

    result = store.list_registered_names()

    assert client_mock.mock_calls == [call.get('/v1/charm')]
    item1, item2 = result
    assert item1.name == 'name1'
    assert not item1.private
    assert item1.status == 'status1'
    assert item2.name == 'name2'
    assert item2.private
    assert item2.status == 'status2'
Example #13
0
def test_get_library(client_mock, config):
    """Get all the information (including content) for a library revision."""
    test_charm_name = 'test-charm-name'
    test_lib_name = 'test-lib-name'
    test_lib_id = 'test-lib-id'
    test_api = 'test-api-version'
    test_patch = 'test-patch-version'
    test_content = 'test content with quite a lot of funny Python code :p'
    test_hash = '1234'

    store = Store(config.charmhub)
    client_mock.get.return_value = {
        'api': test_api,
        'content': test_content,
        'hash': test_hash,
        'library-id': test_lib_id,
        'library-name': test_lib_name,
        'charm-name': test_charm_name,
        'patch': test_patch,
    }

    result_lib = store.get_library(test_charm_name, test_lib_id, test_api)

    assert client_mock.mock_calls == [
        call.get('/v1/charm/libraries/test-charm-name/{}?api={}'.format(test_lib_id, test_api)),
    ]
    assert result_lib.api == test_api
    assert result_lib.content == test_content
    assert result_lib.content_hash == test_hash
    assert result_lib.lib_id == test_lib_id
    assert result_lib.lib_name == test_lib_name
    assert result_lib.charm_name == test_charm_name
    assert result_lib.patch == test_patch
Example #14
0
def test_get_tips_query_combinations(client_mock, config):
    """Use all the combinations to specify what's queried."""
    store = Store(config.charmhub)
    client_mock.post.return_value = {'libraries': []}

    query_info = [
        {'lib_id': 'test-lib-id-1'},
        {'lib_id': 'test-lib-id-2', 'api': 2},
        {'charm_name': 'test-charm-name-3'},
        {'charm_name': 'test-charm-name-4', 'api': 4},
        {'charm_name': 'test-charm-name-5', 'lib_name': 'test-lib-name-5'},
        {'charm_name': 'test-charm-name-6', 'lib_name': 'test-lib-name-6', 'api': 6},
    ]
    store.get_libraries_tips(query_info)

    payload = [
        {'library-id': 'test-lib-id-1'},
        {'library-id': 'test-lib-id-2', 'api': 2},
        {'charm-name': 'test-charm-name-3'},
        {'charm-name': 'test-charm-name-4', 'api': 4},
        {'charm-name': 'test-charm-name-5', 'library-name': 'test-lib-name-5'},
        {'charm-name': 'test-charm-name-6', 'library-name': 'test-lib-name-6', 'api': 6},
    ]
    assert client_mock.mock_calls == [
        call.post('/v1/charm/libraries/bulk', payload),
    ]
Example #15
0
def test_release_with_resources(client_mock, config):
    """Releasing with resources attached."""
    store = Store(config.charmhub)
    r1 = ResourceOption(name="foo", revision=3)
    r2 = ResourceOption(name="bar", revision=17)
    store.release("testname", 123, ["channel1", "channel2"], [r1, r2])

    expected_body = [
        {
            "revision": 123,
            "channel": "channel1",
            "resources": [
                {"name": "foo", "revision": 3},
                {"name": "bar", "revision": 17},
            ],
        },
        {
            "revision": 123,
            "channel": "channel2",
            "resources": [
                {"name": "foo", "revision": 3},
                {"name": "bar", "revision": 17},
            ],
        },
    ]
    assert client_mock.mock_calls == [
        call.post("/v1/charm/testname/releases", expected_body),
    ]
Example #16
0
def test_list_resources_several(client_mock, config):
    """Several items returned."""
    client_mock.get.return_value = {'resources': [
        {
            'name': 'testresource1',
            'optional': True,
            'revision': 123,
            'type': 'file',
        }, {
            'name': 'testresource2',
            'optional': False,
            'revision': 678,
            'type': 'file',
        }
    ]}

    store = Store(config.charmhub)
    result = store.list_resources('some-name')

    (item1, item2) = result

    assert item1.name == 'testresource1'
    assert item1.optional is True
    assert item1.revision == 123
    assert item1.resource_type == 'file'

    assert item2.name == 'testresource2'
    assert item2.optional is False
    assert item2.revision == 678
    assert item2.resource_type == 'file'
Example #17
0
def test_list_revisions_ok(client_mock, config):
    """One revision ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {
        "revisions": [{
            "revision":
            7,
            "version":
            "v7",
            "created-at":
            "2020-06-29T22:11:00.123",
            "status":
            "approved",
            "errors":
            None,
            "bases": [{
                "architecture": "amd64",
                "channel": "20.04",
                "name": "ubuntu"
            }],
        }]
    }

    result = store.list_revisions("some-name")

    assert client_mock.mock_calls == [
        call.get("/v1/charm/some-name/revisions")
    ]

    (item, ) = result
    assert item.revision == 7
    assert item.version == "v7"
    assert item.created_at == parser.parse("2020-06-29T22:11:00.123")
    assert item.status == "approved"
    assert item.errors == []
Example #18
0
def test_list_resource_revisions_ok(client_mock, config):
    """One resource revision ok."""
    store = Store(config.charmhub)
    client_mock.get.return_value = {'revisions': [
        {
            'created-at': '2021-02-11T13:43:22.396606',
            'name': 'otherstuff',
            'revision': 1,
            'sha256': '1bf0399c2de1240777ba73785f1ff1de5331f12853765a0',
            'sha3-384': 'deb9369cb2b9e86ad44160e93da43d240e6388c5dc67b8e2a5a3c2a36a26fe4c89',
            'sha384': 'eaaba6aa119da415e6ad778358a8530c47fefbe3ceced258e8c25530107dc7908e',
            'sha512': 'b8cfe885d49285d8546885167a72fd56ea23480e17c9cdd8e06b45239d79b774c6d6fc09d',
            'size': 500
        },
    ]}

    result = store.list_resource_revisions('charm-name', 'resource-name')

    assert client_mock.mock_calls == [
        call.get('/v1/charm/charm-name/resources/resource-name/revisions')
    ]

    (item,) = result
    assert item.revision == 1
    assert item.created_at == parser.parse('2021-02-11T13:43:22.396606')
    assert item.size == 500
Example #19
0
def test_release_multiple_channels(client_mock, config):
    """Releasing a revision into multiple channels."""
    store = Store(config.charmhub)
    store.release("testname", 123, ["channel1", "channel2", "channel3"], [])

    expected_body = [
        {
            "revision": 123,
            "channel": "channel1",
            "resources": []
        },
        {
            "revision": 123,
            "channel": "channel2",
            "resources": []
        },
        {
            "revision": 123,
            "channel": "channel3",
            "resources": []
        },
    ]
    assert client_mock.mock_calls == [
        call.post("/v1/charm/testname/releases", expected_body),
    ]
Example #20
0
def test_list_resources_several(client_mock, config):
    """Several items returned."""
    client_mock.get.return_value = {
        "resources": [
            {
                "name": "testresource1",
                "optional": True,
                "revision": 123,
                "type": "file",
            },
            {
                "name": "testresource2",
                "optional": False,
                "revision": 678,
                "type": "file",
            },
        ]
    }

    store = Store(config.charmhub)
    result = store.list_resources("some-name")

    (item1, item2) = result

    assert item1.name == "testresource1"
    assert item1.optional is True
    assert item1.revision == 123
    assert item1.resource_type == "file"

    assert item2.name == "testresource2"
    assert item2.optional is False
    assert item2.revision == 678
    assert item2.resource_type == "file"
Example #21
0
def test_upload_polls_status(client_mock, caplog, config):
    """Upload polls status url until the end is indicated."""
    caplog.set_level(logging.DEBUG, logger="charmcraft.commands")
    store = Store(config.charmhub)

    # first and second response, for pushing bytes and let the store know about it
    test_upload_id = "test-upload-id"
    client_mock.push.return_value = test_upload_id
    test_status_url = "https://store.c.c/status"
    client_mock.post.return_value = {"status-url": test_status_url}

    # the status checking response, will answer something not done yet twice, then ok
    test_revision = 123
    test_status_ok = "test-status"
    status_response_1 = {
        "revisions": [{"status": "still-scanning", "revision": None, "errors": None}]
    }
    status_response_2 = {
        "revisions": [{"status": "more-revisions", "revision": None, "errors": None}]
    }
    status_response_3 = {
        "revisions": [
            {"status": test_status_ok, "revision": test_revision, "errors": None}
        ]
    }
    client_mock.get.side_effect = [
        status_response_1,
        status_response_2,
        status_response_3,
    ]

    test_status_resolution = "clean and crispy"
    fake_statuses = {test_status_ok: test_status_resolution}
    with patch.dict(
        "charmcraft.commands.store.store.UPLOAD_ENDING_STATUSES", fake_statuses
    ):
        with patch("charmcraft.commands.store.store.POLL_DELAY", 0.01):
            result = store._upload("/test/endpoint/", "some-filepath")

    # check the status-checking client calls (kept going until third one)
    assert client_mock.mock_calls[2:] == [
        call.get(test_status_url),
        call.get(test_status_url),
        call.get(test_status_url),
    ]

    # check result which must have values from final result
    assert result.ok == test_status_resolution
    assert result.status == test_status_ok
    assert result.revision == test_revision

    # check logs
    expected = [
        "Upload test-upload-id started, got status url https://store.c.c/status",
        "Status checked: " + str(status_response_1),
        "Status checked: " + str(status_response_2),
        "Status checked: " + str(status_response_3),
    ]
    assert expected == [rec.message for rec in caplog.records]
Example #22
0
def test_get_tips_several(client_mock, config):
    """Get info for multiple libs at once."""
    test_charm_name_1 = 'test-charm-name-1'
    test_lib_name_1 = 'test-lib-name-1'
    test_lib_id_1 = 'test-lib-id-1'
    test_api_1 = 'test-api-version-1'
    test_patch_1 = 'test-patch-version-1'
    test_content_1 = 'test content with quite a lot of funny Python code :p'
    test_hash_1 = '1234'

    test_charm_name_2 = 'test-charm-name-2'
    test_lib_name_2 = 'test-lib-name-2'
    test_lib_id_2 = 'test-lib-id-2'
    test_api_2 = 'test-api-version-2'
    test_patch_2 = 'test-patch-version-2'
    test_content_2 = 'more awesome Python code :)'
    test_hash_2 = '5678'

    store = Store(config.charmhub)
    client_mock.post.return_value = {'libraries': [{
        'api': test_api_1,
        'content': test_content_1,
        'hash': test_hash_1,
        'library-id': test_lib_id_1,
        'library-name': test_lib_name_1,
        'charm-name': test_charm_name_1,
        'patch': test_patch_1,
    }, {
        'api': test_api_2,
        'content': test_content_2,
        'hash': test_hash_2,
        'library-id': test_lib_id_2,
        'library-name': test_lib_name_2,
        'charm-name': test_charm_name_2,
        'patch': test_patch_2,
    }]}

    query_info = [
        {'lib_id': test_lib_id_1},
        {'lib_id': test_lib_id_2},
    ]
    result = store.get_libraries_tips(query_info)

    payload = [
        {'library-id': test_lib_id_1},
        {'library-id': test_lib_id_2},
    ]
    assert client_mock.mock_calls == [
        call.post('/v1/charm/libraries/bulk', payload),
    ]
    expected = {
        (test_lib_id_1, test_api_1): Library(
            api=test_api_1, content=test_content_1, content_hash=test_hash_1, lib_id=test_lib_id_1,
            lib_name=test_lib_name_1, charm_name=test_charm_name_1, patch=test_patch_1),
        (test_lib_id_2, test_api_2): Library(
            api=test_api_2, content=test_content_2, content_hash=test_hash_2, lib_id=test_lib_id_2,
            lib_name=test_lib_name_2, charm_name=test_charm_name_2, patch=test_patch_2),
    }
    assert result == expected
Example #23
0
def test_logout(client_mock, config):
    """Simple logout case."""
    store = Store(config.charmhub)
    result = store.logout()
    assert client_mock.mock_calls == [
        call.clear_credentials(),
    ]
    assert result is None
Example #24
0
def test_register_name(client_mock, config):
    """Simple register case."""
    store = Store(config.charmhub)
    result = store.register_name('testname', 'stuff')

    assert client_mock.mock_calls == [
        call.post('/v1/charm', {'name': 'testname', 'type': 'stuff'}),
    ]
    assert result is None
Example #25
0
def test_release_simple(client_mock, config):
    """Releasing a revision into one channel."""
    store = Store(config.charmhub)
    store.release("testname", 123, ["somechannel"], [])

    expected_body = [{"revision": 123, "channel": "somechannel", "resources": []}]
    assert client_mock.mock_calls == [
        call.post("/v1/charm/testname/releases", expected_body),
    ]
Example #26
0
def test_register_name(client_mock, config):
    """Simple register case."""
    store = Store(config.charmhub)
    result = store.register_name("testname", "stuff")

    assert client_mock.mock_calls == [
        call.post("/v1/charm", {"name": "testname", "type": "stuff"}),
    ]
    assert result is None
Example #27
0
def test_register_name(client_mock):
    """Simple register case."""
    store = Store()
    result = store.register_name('testname')

    assert client_mock.mock_calls == [
        call.post('/v1/charm', {'name': 'testname'}),
    ]
    assert result is None
Example #28
0
def test_login(client_mock, config):
    """Simple login case."""
    store = Store(config.charmhub)
    result = store.login()
    assert client_mock.mock_calls == [
        call.clear_credentials(),
        call.get('/v1/whoami'),
    ]
    assert result is None
Example #29
0
def test_release_simple(client_mock, config):
    """Releasing a revision into one channel."""
    store = Store(config.charmhub)
    store.release('testname', 123, ['somechannel'])

    expected_body = [{'revision': 123, 'channel': 'somechannel'}]
    assert client_mock.mock_calls == [
        call.post('/v1/charm/testname/releases', expected_body),
    ]
Example #30
0
def test_list_resource_revisions_several(client_mock, config):
    """Several items returned."""
    client_mock.get.return_value = {
        "revisions": [
            {
                "created-at":
                "2021-02-11T13:43:22.396606",
                "name":
                "otherstuff",
                "revision":
                1,
                "sha256":
                "1bf0399c2de1240777ba73785f1ff1de5331f12853765a0",
                "sha3-384":
                "deb9369cb2b9e86ad44160e93da43d240e6388c5dc67b8e2a5a3c2a36a26fe4c89",
                "sha384":
                "eaaba6aa119da415e6ad778358a8530c47fefbe3ceced258e8c25530107dc7908e",
                "sha512":
                ("b8cfe885d49285d8546885167a72fd56ea23480e17c9cdd8e06b45239d79b774c6d6fc09d"
                 ),
                "size":
                500,
            },
            {
                "created-at":
                "2021-02-11T14:23:55.659148",
                "name":
                "otherstuff",
                "revision":
                2,
                "sha256":
                "73785f1ff1de5331f12853765a01bf0399c2de1240777ba",
                "sha3-384":
                "60e93da43d240e6388c5dc67b8e2a5a3c2a36a26fe4c89deb9369cb2b5e86ad441",
                "sha384":
                "778358a8530c47fefbe3ceced258e8c25530107dc7908eeaaba6aa119dad15e6ad",
                "sha512":
                ("05167a72fd56ea23480e17c9cdd8e06b45239d79b774c6d6fc09db8cfe885d49285d8547c"
                 ),
                "size":
                420,
            },
        ]
    }

    store = Store(config.charmhub)
    result = store.list_resource_revisions("charm-name", "resource-name")

    (item1, item2) = result

    assert item1.revision == 1
    assert item1.created_at == parser.parse("2021-02-11T13:43:22.396606")
    assert item1.size == 500

    assert item2.revision == 2
    assert item2.created_at == parser.parse("2021-02-11T14:23:55.659148")
    assert item2.size == 420