A Gleam WebSocket consumer for AT Protocol Jetstream events.

fix tests, format

Changed files
+137 -90
.github
workflows
src
test
+23
.github/workflows/test.yml
···
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "28" 18 + gleam-version: "1.13.0" 19 + rebar3-version: "3" 20 + # elixir-version: "1" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4 -1
src/goose.gleam
··· 95 let mut_params = case config.max_message_size_bytes { 96 option.None -> mut_params 97 option.Some(size_val) -> 98 - list.append(["maxMessageSizeBytes=" <> string.inspect(size_val)], mut_params) 99 } 100 101 // Add compress parameter (always include it)
··· 95 let mut_params = case config.max_message_size_bytes { 96 option.None -> mut_params 97 option.Some(size_val) -> 98 + list.append( 99 + ["maxMessageSizeBytes=" <> string.inspect(size_val)], 100 + mut_params, 101 + ) 102 } 103 104 // Add compress parameter (always include it)
+110 -89
test/goose_test.gleam
··· 11 let config = goose.default_config() 12 let url = goose.build_url(config) 13 14 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe" 15 } 16 17 // Test build_url with wanted collections 18 pub fn build_url_with_collections_test() { 19 - let config = goose.JetstreamConfig( 20 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 21 - wanted_collections: ["app.bsky.feed.post", "app.bsky.feed.like"], 22 - wanted_dids: [], 23 - cursor: option.None, 24 - max_message_size_bytes: option.None, 25 - compress: False, 26 - require_hello: False, 27 - ) 28 let url = goose.build_url(config) 29 30 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.like&wantedCollections=app.bsky.feed.post" 31 } 32 33 // Test build_url with wanted DIDs 34 pub fn build_url_with_dids_test() { 35 - let config = goose.JetstreamConfig( 36 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 37 - wanted_collections: [], 38 - wanted_dids: ["did:plc:example123", "did:plc:example456"], 39 - cursor: option.None, 40 - max_message_size_bytes: option.None, 41 - compress: False, 42 - require_hello: False, 43 - ) 44 let url = goose.build_url(config) 45 46 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedDids=did:plc:example456&wantedDids=did:plc:example123" 47 } 48 49 // Test build_url with both collections and DIDs 50 pub fn build_url_with_both_test() { 51 - let config = goose.JetstreamConfig( 52 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 53 - wanted_collections: ["app.bsky.feed.post"], 54 - wanted_dids: ["did:plc:example123"], 55 - cursor: option.None, 56 - max_message_size_bytes: option.None, 57 - compress: False, 58 - require_hello: False, 59 - ) 60 let url = goose.build_url(config) 61 62 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123" 63 } 64 65 // Test build_url with cursor 66 pub fn build_url_with_cursor_test() { 67 - let config = goose.JetstreamConfig( 68 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 69 - wanted_collections: [], 70 - wanted_dids: [], 71 - cursor: option.Some(1234567890123456), 72 - max_message_size_bytes: option.None, 73 - compress: False, 74 - require_hello: False, 75 - ) 76 let url = goose.build_url(config) 77 78 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?cursor=1234567890123456" 79 } 80 81 // Test build_url with max_message_size_bytes 82 pub fn build_url_with_max_size_test() { 83 - let config = goose.JetstreamConfig( 84 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 85 - wanted_collections: [], 86 - wanted_dids: [], 87 - cursor: option.None, 88 - max_message_size_bytes: option.Some(1048576), 89 - compress: False, 90 - require_hello: False, 91 - ) 92 let url = goose.build_url(config) 93 94 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?maxMessageSizeBytes=1048576" 95 } 96 97 // Test build_url with compress enabled 98 pub fn build_url_with_compress_test() { 99 - let config = goose.JetstreamConfig( 100 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 101 - wanted_collections: [], 102 - wanted_dids: [], 103 - cursor: option.None, 104 - max_message_size_bytes: option.None, 105 - compress: True, 106 - require_hello: False, 107 - ) 108 let url = goose.build_url(config) 109 110 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?compress=true" 111 } 112 113 // Test build_url with require_hello enabled 114 pub fn build_url_with_require_hello_test() { 115 - let config = goose.JetstreamConfig( 116 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 117 - wanted_collections: [], 118 - wanted_dids: [], 119 - cursor: option.None, 120 - max_message_size_bytes: option.None, 121 - compress: False, 122 - require_hello: True, 123 - ) 124 let url = goose.build_url(config) 125 126 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?requireHello=true" 127 } 128 129 // Test build_url with all options combined 130 pub fn build_url_with_all_options_test() { 131 - let config = goose.JetstreamConfig( 132 - endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 133 - wanted_collections: ["app.bsky.feed.post"], 134 - wanted_dids: ["did:plc:example123"], 135 - cursor: option.Some(9876543210), 136 - max_message_size_bytes: option.Some(2097152), 137 - compress: True, 138 - require_hello: True, 139 - ) 140 let url = goose.build_url(config) 141 142 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123&cursor=9876543210&maxMessageSizeBytes=2097152&compress=true&requireHello=true" 143 } 144 145 // Test parsing a commit event (create operation with record) 146 pub fn parse_commit_event_create_test() { 147 - let json = "{\"did\":\"did:plc:test123\",\"time_us\":1234567890,\"commit\":{\"rev\":\"abc123\",\"operation\":\"create\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"post123\",\"record\":{\"text\":\"Hello world\"},\"cid\":\"cid123\"}}" 148 149 let event = goose.parse_event(json) 150 151 case event { 152 goose.CommitEvent(did, time_us, commit) -> { 153 assert did == "did:plc:test123" 154 - assert time_us == 1234567890 155 assert commit.rev == "abc123" 156 assert commit.operation == "create" 157 assert commit.collection == "app.bsky.feed.post" ··· 163 164 // Test parsing a commit event (delete operation without record) 165 pub fn parse_commit_event_delete_test() { 166 - let json = "{\"did\":\"did:plc:test456\",\"time_us\":9876543210,\"commit\":{\"rev\":\"xyz789\",\"operation\":\"delete\",\"collection\":\"app.bsky.feed.like\",\"rkey\":\"like456\"}}" 167 168 let event = goose.parse_event(json) 169 170 case event { 171 goose.CommitEvent(did, time_us, commit) -> { 172 assert did == "did:plc:test456" 173 - assert time_us == 9876543210 174 assert commit.rev == "xyz789" 175 assert commit.operation == "delete" 176 assert commit.collection == "app.bsky.feed.like" ··· 182 183 // Test parsing an identity event 184 pub fn parse_identity_event_test() { 185 - let json = "{\"did\":\"did:plc:identity123\",\"time_us\":1111111111,\"identity\":{\"did\":\"did:plc:identity123\",\"handle\":\"alice.bsky.social\",\"seq\":42,\"time\":\"2024-01-01T00:00:00Z\"}}" 186 187 let event = goose.parse_event(json) 188 189 case event { 190 goose.IdentityEvent(did, time_us, identity) -> { 191 assert did == "did:plc:identity123" 192 - assert time_us == 1111111111 193 assert identity.did == "did:plc:identity123" 194 assert identity.handle == "alice.bsky.social" 195 assert identity.seq == 42 ··· 201 202 // Test parsing an account event 203 pub fn parse_account_event_test() { 204 - let json = "{\"did\":\"did:plc:account789\",\"time_us\":2222222222,\"account\":{\"active\":true,\"did\":\"did:plc:account789\",\"seq\":99,\"time\":\"2024-01-02T00:00:00Z\"}}" 205 206 let event = goose.parse_event(json) 207 208 case event { 209 goose.AccountEvent(did, time_us, account) -> { 210 assert did == "did:plc:account789" 211 - assert time_us == 2222222222 212 assert account.active == True 213 assert account.did == "did:plc:account789" 214 assert account.seq == 99
··· 11 let config = goose.default_config() 12 let url = goose.build_url(config) 13 14 + assert url 15 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=false&requireHello=false" 16 } 17 18 // Test build_url with wanted collections 19 pub fn build_url_with_collections_test() { 20 + let config = 21 + goose.JetstreamConfig( 22 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 23 + wanted_collections: ["app.bsky.feed.post", "app.bsky.feed.like"], 24 + wanted_dids: [], 25 + cursor: option.None, 26 + max_message_size_bytes: option.None, 27 + compress: False, 28 + require_hello: False, 29 + ) 30 let url = goose.build_url(config) 31 32 + assert url 33 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.like&wantedCollections=app.bsky.feed.post&compress=false&requireHello=false" 34 } 35 36 // Test build_url with wanted DIDs 37 pub fn build_url_with_dids_test() { 38 + let config = 39 + goose.JetstreamConfig( 40 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 41 + wanted_collections: [], 42 + wanted_dids: ["did:plc:example123", "did:plc:example456"], 43 + cursor: option.None, 44 + max_message_size_bytes: option.None, 45 + compress: False, 46 + require_hello: False, 47 + ) 48 let url = goose.build_url(config) 49 50 + assert url 51 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedDids=did:plc:example456&wantedDids=did:plc:example123&compress=false&requireHello=false" 52 } 53 54 // Test build_url with both collections and DIDs 55 pub fn build_url_with_both_test() { 56 + let config = 57 + goose.JetstreamConfig( 58 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 59 + wanted_collections: ["app.bsky.feed.post"], 60 + wanted_dids: ["did:plc:example123"], 61 + cursor: option.None, 62 + max_message_size_bytes: option.None, 63 + compress: False, 64 + require_hello: False, 65 + ) 66 let url = goose.build_url(config) 67 68 + assert url 69 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123&compress=false&requireHello=false" 70 } 71 72 // Test build_url with cursor 73 pub fn build_url_with_cursor_test() { 74 + let config = 75 + goose.JetstreamConfig( 76 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 77 + wanted_collections: [], 78 + wanted_dids: [], 79 + cursor: option.Some(1_234_567_890_123_456), 80 + max_message_size_bytes: option.None, 81 + compress: False, 82 + require_hello: False, 83 + ) 84 let url = goose.build_url(config) 85 86 + assert url 87 + == "wss://jetstream2.us-east.bsky.network/subscribe?cursor=1234567890123456&compress=false&requireHello=false" 88 } 89 90 // Test build_url with max_message_size_bytes 91 pub fn build_url_with_max_size_test() { 92 + let config = 93 + goose.JetstreamConfig( 94 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 95 + wanted_collections: [], 96 + wanted_dids: [], 97 + cursor: option.None, 98 + max_message_size_bytes: option.Some(1_048_576), 99 + compress: False, 100 + require_hello: False, 101 + ) 102 let url = goose.build_url(config) 103 104 + assert url 105 + == "wss://jetstream2.us-east.bsky.network/subscribe?maxMessageSizeBytes=1048576&compress=false&requireHello=false" 106 } 107 108 // Test build_url with compress enabled 109 pub fn build_url_with_compress_test() { 110 + let config = 111 + goose.JetstreamConfig( 112 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 113 + wanted_collections: [], 114 + wanted_dids: [], 115 + cursor: option.None, 116 + max_message_size_bytes: option.None, 117 + compress: True, 118 + require_hello: False, 119 + ) 120 let url = goose.build_url(config) 121 122 + assert url 123 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=true&requireHello=false" 124 } 125 126 // Test build_url with require_hello enabled 127 pub fn build_url_with_require_hello_test() { 128 + let config = 129 + goose.JetstreamConfig( 130 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 131 + wanted_collections: [], 132 + wanted_dids: [], 133 + cursor: option.None, 134 + max_message_size_bytes: option.None, 135 + compress: False, 136 + require_hello: True, 137 + ) 138 let url = goose.build_url(config) 139 140 + assert url 141 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=false&requireHello=true" 142 } 143 144 // Test build_url with all options combined 145 pub fn build_url_with_all_options_test() { 146 + let config = 147 + goose.JetstreamConfig( 148 + endpoint: "wss://jetstream2.us-east.bsky.network/subscribe", 149 + wanted_collections: ["app.bsky.feed.post"], 150 + wanted_dids: ["did:plc:example123"], 151 + cursor: option.Some(9_876_543_210), 152 + max_message_size_bytes: option.Some(2_097_152), 153 + compress: True, 154 + require_hello: True, 155 + ) 156 let url = goose.build_url(config) 157 158 + assert url 159 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123&cursor=9876543210&maxMessageSizeBytes=2097152&compress=true&requireHello=true" 160 } 161 162 // Test parsing a commit event (create operation with record) 163 pub fn parse_commit_event_create_test() { 164 + let json = 165 + "{\"did\":\"did:plc:test123\",\"time_us\":1234567890,\"commit\":{\"rev\":\"abc123\",\"operation\":\"create\",\"collection\":\"app.bsky.feed.post\",\"rkey\":\"post123\",\"record\":{\"text\":\"Hello world\"},\"cid\":\"cid123\"}}" 166 167 let event = goose.parse_event(json) 168 169 case event { 170 goose.CommitEvent(did, time_us, commit) -> { 171 assert did == "did:plc:test123" 172 + assert time_us == 1_234_567_890 173 assert commit.rev == "abc123" 174 assert commit.operation == "create" 175 assert commit.collection == "app.bsky.feed.post" ··· 181 182 // Test parsing a commit event (delete operation without record) 183 pub fn parse_commit_event_delete_test() { 184 + let json = 185 + "{\"did\":\"did:plc:test456\",\"time_us\":9876543210,\"commit\":{\"rev\":\"xyz789\",\"operation\":\"delete\",\"collection\":\"app.bsky.feed.like\",\"rkey\":\"like456\"}}" 186 187 let event = goose.parse_event(json) 188 189 case event { 190 goose.CommitEvent(did, time_us, commit) -> { 191 assert did == "did:plc:test456" 192 + assert time_us == 9_876_543_210 193 assert commit.rev == "xyz789" 194 assert commit.operation == "delete" 195 assert commit.collection == "app.bsky.feed.like" ··· 201 202 // Test parsing an identity event 203 pub fn parse_identity_event_test() { 204 + let json = 205 + "{\"did\":\"did:plc:identity123\",\"time_us\":1111111111,\"identity\":{\"did\":\"did:plc:identity123\",\"handle\":\"alice.bsky.social\",\"seq\":42,\"time\":\"2024-01-01T00:00:00Z\"}}" 206 207 let event = goose.parse_event(json) 208 209 case event { 210 goose.IdentityEvent(did, time_us, identity) -> { 211 assert did == "did:plc:identity123" 212 + assert time_us == 1_111_111_111 213 assert identity.did == "did:plc:identity123" 214 assert identity.handle == "alice.bsky.social" 215 assert identity.seq == 42 ··· 221 222 // Test parsing an account event 223 pub fn parse_account_event_test() { 224 + let json = 225 + "{\"did\":\"did:plc:account789\",\"time_us\":2222222222,\"account\":{\"active\":true,\"did\":\"did:plc:account789\",\"seq\":99,\"time\":\"2024-01-02T00:00:00Z\"}}" 226 227 let event = goose.parse_event(json) 228 229 case event { 230 goose.AccountEvent(did, time_us, account) -> { 231 assert did == "did:plc:account789" 232 + assert time_us == 2_222_222_222 233 assert account.active == True 234 assert account.did == "did:plc:account789" 235 assert account.seq == 99