Esempio n. 1
0
def test_update_publish_items_invalid_item(db, auth_header):
    """PUTting an item without object_key or link_to fails validation."""

    publish_id = "11224567-e89b-12d3-a456-426614174000"

    publish = Publish(id=uuid.UUID("{%s}" % publish_id),
                      env="test",
                      state="PENDING")

    with TestClient(app) as client:
        # ensure a publish object exists
        db.add(publish)
        db.commit()

        # Try to add an item to it
        r = client.put(
            "/test/publish/%s" % publish_id,
            json=[{
                "web_uri": "/uri1"
            }],
            headers=auth_header(roles=["test-publisher"]),
        )

    expected_item = {
        "web_uri": "/uri1",
        "object_key": "",
        "content_type": "",
        "link_to": "",
    }

    # It should have failed with 400
    assert r.status_code == 400
    assert r.json() == {
        "detail": ["No object key or link target: %s" % expected_item]
    }
Esempio n. 2
0
def test_update_publish_items_invalid_publish(db, auth_header):
    """PUTting items on a completed publish fails with code 409."""

    publish_id = "11224567-e89b-12d3-a456-426614174000"

    publish = Publish(id=uuid.UUID("{%s}" % publish_id),
                      env="test",
                      state="COMPLETE")

    with TestClient(app) as client:
        # ensure a publish object exists
        db.add(publish)
        db.commit()

        # Try to add some items to it
        r = client.put(
            "/test/publish/%s" % publish_id,
            json=[
                {
                    "web_uri": "/uri1",
                    "object_key": "1" * 64,
                },
            ],
            headers=auth_header(roles=["test-publisher"]),
        )

    # It should have failed with 409
    assert r.status_code == 409
    assert r.json() == {
        "detail": "Publish %s in unexpected state, 'COMPLETE'" % publish_id
    }
Esempio n. 3
0
def test_publish_task_before_update(db):
    """Changing object states updates timestamp."""

    publish_id = "11224567-e89b-12d3-a456-426614174000"
    publish = Publish(id=uuid.UUID(publish_id), env="test", state="PENDING")

    task_id = "8d8a4692-c89b-4b57-840f-b3f0166148d2"
    task = Task(
        id=task_id,
        publish_id=uuid.UUID(publish_id),
        state="NOT_STARTED",
    )

    db.add(publish)
    db.add(task)
    db.commit()

    # Updated should initially be null
    assert publish.updated is None
    assert task.updated is None

    # Change state of publish and task
    publish.state = "COMMITTING"
    task.state = "IN_PROGRESS"
    db.commit()

    # Updated should now hold datetime objects
    p_updated = publish.updated
    assert isinstance(p_updated, datetime)

    t_updated = task.updated
    assert isinstance(t_updated, datetime)

    # Change state of publish and task again
    publish.state = "COMMITTED"
    task.state = "COMPLETE"
    db.commit()

    # Updated should now hold different datetime objects
    assert isinstance(publish.updated, datetime)
    assert p_updated != publish.updated

    assert isinstance(task.updated, datetime)
    assert t_updated != task.updated
Esempio n. 4
0
def make_publish(mode: str = None, db: Session = deps.db):
    p = Publish(id=TEST_UUID, env="test", state="PENDING")
    db.add(p)

    if mode == "rollback":
        db.rollback()
    elif mode == "commit":
        db.commit()
    elif mode == "raise":
        raise HTTPException(500)
Esempio n. 5
0
def test_update_publish_items_path_normalization(db, auth_header):
    """URI and link target paths are normalized in PUT items."""

    publish_id = "11224567-e89b-12d3-a456-426614174000"

    publish = Publish(id=uuid.UUID("{%s}" % publish_id),
                      env="test",
                      state="PENDING")

    with TestClient(app) as client:
        # Ensure a publish object exists
        db.add(publish)
        db.commit()

        # Add an item to it with some messy paths
        r = client.put(
            "/test/publish/%s" % publish_id,
            json=[
                {
                    "web_uri": "some/path",
                    "object_key": "1" * 64
                },
                {
                    "web_uri": "link/to/some/path",
                    "link_to": "/some/path"
                },
            ],
            headers=auth_header(roles=["test-publisher"]),
        )

    # It should have succeeded
    assert r.ok

    # Publish object should now have matching items
    db.refresh(publish)

    item_dicts = [{
        "web_uri": item.web_uri,
        "object_key": item.object_key,
        "link_to": item.link_to,
    } for item in publish.items]

    # Should have stored normalized web_uri and link_to paths
    assert item_dicts == [
        {
            "web_uri": "/some/path",
            "object_key": "1" * 64,
            "link_to": ""
        },
        {
            "web_uri": "/link/to/some/path",
            "object_key": "",
            "link_to": "/some/path",
        },
    ]
Esempio n. 6
0
def test_cleanup_mixed(caplog, db):
    """Cleanup manipulates objects in the expected manner."""

    logging.getLogger("exodus-gw").setLevel(logging.INFO)

    # Note: datetimes in this test assume the default timeout values
    # are used.

    now = datetime.utcnow()
    half_day_ago = now - timedelta(hours=12)
    two_days_ago = now - timedelta(days=2)
    eight_days_ago = now - timedelta(days=8)
    twenty_days_ago = now - timedelta(days=30)

    # Some objects with missing timestamps.
    p1_missing_ts = Publish(id=uuid.uuid4(),
                            env="test",
                            state=PublishStates.failed,
                            updated=None)
    p2_missing_ts = Publish(
        id=uuid.uuid4(),
        env="test2",
        state=PublishStates.committed,
        updated=None,
    )
    t1_missing_ts = Task(
        id=uuid.uuid4(),
        publish_id=p1_missing_ts.id,
        state=TaskStates.failed,
        updated=None,
    )

    # Some publishes which seem to be abandoned.
    p1_abandoned = Publish(
        id=uuid.uuid4(),
        env="test",
        state=PublishStates.pending,
        updated=eight_days_ago,
        items=[
            Item(web_uri="/1", object_key="abc", link_to=""),
            Item(web_uri="/2", object_key="aabbcc", link_to=""),
        ],
    )
    p2_abandoned = Publish(
        id=uuid.uuid4(),
        env="test",
        state=PublishStates.committing,
        updated=twenty_days_ago,
    )
    t1_abandoned = Task(
        id=uuid.uuid4(),
        publish_id=p1_abandoned.id,
        state=TaskStates.in_progress,
        updated=eight_days_ago,
    )

    # Some objects which are old enough to be cleaned up.
    p1_old = Publish(
        id=uuid.uuid4(),
        env="test2",
        state=PublishStates.committed,
        updated=twenty_days_ago,
        items=[
            Item(web_uri="/1", object_key="abc", link_to=""),
            Item(web_uri="/2", object_key="aabbcc", link_to=""),
        ],
    )
    p2_old = Publish(
        id=uuid.uuid4(),
        env="test3",
        state=PublishStates.failed,
        updated=twenty_days_ago,
    )
    t1_old = Task(
        id=uuid.uuid4(),
        publish_id=p1_old.id,
        state=TaskStates.failed,
        updated=twenty_days_ago,
    )
    # (Because these objects will be deleted, we need to keep their ids separately.)
    p1_old_id = p1_old.id
    p2_old_id = p2_old.id
    t1_old_id = t1_old.id

    # And finally some recent objects which should not be touched at all.
    p1_recent = Publish(
        id=uuid.uuid4(),
        env="test3",
        state=PublishStates.pending,
        updated=half_day_ago,
    )
    t1_recent = Task(
        id=uuid.uuid4(),
        publish_id=p1_recent.id,
        state=TaskStates.complete,
        updated=two_days_ago,
    )

    # self.fix_abandoned_publishes()
    # self.clean_old_data()

    db.add_all([
        p1_missing_ts,
        p2_missing_ts,
        t1_missing_ts,
        p1_abandoned,
        p2_abandoned,
        t1_abandoned,
        p1_old,
        p2_old,
        t1_old,
        p1_recent,
        t1_recent,
    ])
    db.commit()

    # Should run successfully
    cleanup()

    # Make sure we reload anything which has changed
    db.expire_all()

    # Missing timestamps should now be filled in (fuzzy comparison as exact
    # time is not set)
    assert (t1_missing_ts.updated - now) < timedelta(seconds=10)
    assert (p1_missing_ts.updated - now) < timedelta(seconds=10)
    assert (p2_missing_ts.updated - now) < timedelta(seconds=10)

    # The abandoned objects should now be marked as failed
    assert p1_abandoned.state == PublishStates.failed
    assert p2_abandoned.state == PublishStates.failed
    assert t1_abandoned.state == TaskStates.failed

    # The old objects should no longer exist.
    with pytest.raises(ObjectDeletedError):
        p1_old.id
    with pytest.raises(ObjectDeletedError):
        p2_old.id
    with pytest.raises(ObjectDeletedError):
        t1_old.id

    # Other objects should still exist as they were.
    assert p1_recent.state == PublishStates.pending
    assert t1_recent.state == TaskStates.complete

    # It should have logged exactly what it did.
    assert sorted(caplog.messages) == sorted([
        ####################################################
        # Fixed timestamps
        "Task %s: setting updated" % (t1_missing_ts.id, ),
        "Publish %s: setting updated" % (p1_missing_ts.id, ),
        "Publish %s: setting updated" % (p2_missing_ts.id, ),
        ####################################################
        # Abandoned objects
        "Task %s: marking as failed (last updated: %s)" %
        (t1_abandoned.id, eight_days_ago),
        "Publish %s: marking as failed (last updated: %s)" %
        (p1_abandoned.id, eight_days_ago),
        "Publish %s: marking as failed (last updated: %s)" %
        (p2_abandoned.id, twenty_days_ago),
        ####################################################
        # Deleted old stuff
        "Task %s: cleaning old data (last updated: %s)" %
        (t1_old_id, twenty_days_ago),
        "Publish %s: cleaning old data (last updated: %s)" %
        (p1_old_id, twenty_days_ago),
        "Publish %s: cleaning old data (last updated: %s)" %
        (p2_old_id, twenty_days_ago),
        ####################################################
        # Completed cleanup
        "Scheduled cleanup has completed",
    ])
Esempio n. 7
0
def test_update_publish_items_typical(db, auth_header):
    """PUTting some items on a publish creates expected objects in DB."""

    publish_id = "11224567-e89b-12d3-a456-426614174000"

    publish = Publish(id=uuid.UUID("{%s}" % publish_id),
                      env="test",
                      state="PENDING")

    with TestClient(app) as client:
        # Ensure a publish object exists
        db.add(publish)
        db.commit()

        # Try to add some items to it
        r = client.put(
            "/test/publish/%s" % publish_id,
            json=[
                {
                    "web_uri": "/uri1",
                    "object_key": "1" * 64,
                    "content_type": "application/octet-stream",
                },
                {
                    "web_uri": "/uri2",
                    "object_key": "2" * 64,
                    "content_type": "application/octet-stream",
                },
                {
                    "web_uri": "/uri3",
                    "link_to": "/uri1",
                },
                {
                    "web_uri": "/uri4",
                    "object_key": "absent",
                },
            ],
            headers=auth_header(roles=["test-publisher"]),
        )

    # It should have succeeded
    assert r.ok

    # Publish object should now have matching items
    db.refresh(publish)

    items = sorted(publish.items, key=lambda item: item.web_uri)
    item_dicts = [{
        "web_uri": item.web_uri,
        "object_key": item.object_key,
        "content_type": item.content_type,
        "link_to": item.link_to,
    } for item in items]

    # Should have stored exactly what we asked for
    assert item_dicts == [
        {
            "web_uri": "/uri1",
            "object_key": "1" * 64,
            "content_type": "application/octet-stream",
            "link_to": "",
        },
        {
            "web_uri": "/uri2",
            "object_key": "2" * 64,
            "content_type": "application/octet-stream",
            "link_to": "",
        },
        {
            "web_uri": "/uri3",
            "object_key": "",
            "content_type": "",
            "link_to": "/uri1",
        },
        {
            "web_uri": "/uri4",
            "object_key": "absent",
            "content_type": "",
            "link_to": "",
        },
    ]