File
Blob: src/workerd/api/actor-state-iocontext-test.c++
| 1 | // Copyright (c) 2017-2023 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #include <workerd/api/actor-state.h> |
| 6 | #include <workerd/io/actor-id.h> |
| 7 | #include <workerd/tests/test-fixture.h> |
| 8 | |
| 9 | #include <kj/encoding.h> |
| 10 | #include <kj/test.h> |
| 11 | |
| 12 | #include <algorithm> |
| 13 | |
| 14 | namespace workerd::api { |
| 15 | namespace { |
| 16 | |
| 17 | using workerd::TestFixture; |
| 18 | |
| 19 | bool contains(kj::StringPtr haystack, kj::StringPtr needle) { |
| 20 | return std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end()) != |
| 21 | haystack.end(); |
| 22 | } |
| 23 | |
| 24 | class MockActorId: public ActorIdFactory::ActorId { |
| 25 | public: |
| 26 | MockActorId(kj::String id): id(kj::mv(id)) {} |
| 27 | kj::String toString() const override { |
| 28 | return kj::str("MockActorId<", id, ">"); |
| 29 | } |
| 30 | |
| 31 | kj::Maybe<kj::StringPtr> getName() const override { |
| 32 | return kj::none; |
| 33 | } |
| 34 | |
| 35 | kj::Maybe<kj::StringPtr> getJurisdiction() const override { |
| 36 | return kj::none; |
| 37 | } |
| 38 | |
| 39 | bool equals(const ActorId& other) const override { |
| 40 | return false; |
| 41 | } |
| 42 | |
| 43 | kj::Own<ActorId> clone() const override { |
| 44 | return kj::heap<MockActorId>(kj::heapString(id)); |
| 45 | } |
| 46 | |
| 47 | virtual ~MockActorId() {}; |
| 48 | |
| 49 | private: |
| 50 | kj::String id; |
| 51 | }; |
| 52 | |
| 53 | void runBadDeserialization(jsg::Lock& lock, kj::StringPtr expectedId) { |
| 54 | // FF = kVersion token, 0E = version 15, 06 = an unknown tag value |
| 55 | kj::StringPtr invalidV8Hex = "FF0E06"_kj; |
| 56 | auto invalidV8Value = kj::decodeHex(invalidV8Hex.asArray()); |
| 57 | try { |
| 58 | deserializeV8Value(lock, "some-key"_kj, invalidV8Value); |
| 59 | KJ_FAIL_ASSERT("deserializeV8Value should have failed."); |
| 60 | } catch (kj::Exception& ex) { |
| 61 | if (ex.getDescription().startsWith("actor storage deserialization failed")) { |
| 62 | KJ_ASSERT(contains(ex.getDescription(), expectedId)); |
| 63 | } else { |
| 64 | throw; |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void runBadDeserializationInIoContext(TestFixture& fixture, kj::StringPtr expectedId) { |
| 70 | fixture.runInIoContext( |
| 71 | [expectedId](const workerd::TestFixture::Environment& env) -> kj::Promise<void> { |
| 72 | runBadDeserialization(env.lock, expectedId); |
| 73 | return kj::READY_NOW; |
| 74 | }); |
| 75 | } |
| 76 | |
| 77 | // TODO(maybe) It would be nice to have a test that tests the case when there's no IoContext, |
| 78 | // but that's a royal pain to set up in this test file we'd basically only test that we don't |
| 79 | // crash, which the actor-state-test.c++ does for us. |
| 80 | |
| 81 | KJ_TEST("no actor specified") { |
| 82 | TestFixture fixture; |
| 83 | runBadDeserializationInIoContext(fixture, "actorId = ;"_kj); |
| 84 | } |
| 85 | |
| 86 | KJ_TEST("actor specified with string id") { |
| 87 | Worker::Actor::Id id = kj::str("testActorId"); |
| 88 | TestFixture fixture(TestFixture::SetupParams{.actorId = kj::mv(id)}); |
| 89 | runBadDeserializationInIoContext(fixture, "actorId = testActorId;"_kj); |
| 90 | } |
| 91 | |
| 92 | KJ_TEST("actor specified with ActorId object") { |
| 93 | kj::Own<ActorIdFactory::ActorId> mockActorId = kj::heap<MockActorId>(kj::str("testActorId")); |
| 94 | Worker::Actor::Id id = kj::mv(mockActorId); |
| 95 | TestFixture fixture(TestFixture::SetupParams{ |
| 96 | .actorId = kj::mv(id), |
| 97 | }); |
| 98 | runBadDeserializationInIoContext(fixture, "actorId = MockActorId<testActorId>;"_kj); |
| 99 | } |
| 100 | |
| 101 | } // namespace |
| 102 | } // namespace workerd::api |