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 95 let mut_params = case config.max_message_size_bytes { 96 96 option.None -> mut_params 97 97 option.Some(size_val) -> 98 - list.append(["maxMessageSizeBytes=" <> string.inspect(size_val)], mut_params) 98 + list.append( 99 + ["maxMessageSizeBytes=" <> string.inspect(size_val)], 100 + mut_params, 101 + ) 99 102 } 100 103 101 104 // Add compress parameter (always include it)
+110 -89
test/goose_test.gleam
··· 11 11 let config = goose.default_config() 12 12 let url = goose.build_url(config) 13 13 14 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe" 14 + assert url 15 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=false&requireHello=false" 15 16 } 16 17 17 18 // Test build_url with wanted collections 18 19 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 - ) 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 + ) 28 30 let url = goose.build_url(config) 29 31 30 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.like&wantedCollections=app.bsky.feed.post" 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" 31 34 } 32 35 33 36 // Test build_url with wanted DIDs 34 37 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 - ) 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 + ) 44 48 let url = goose.build_url(config) 45 49 46 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedDids=did:plc:example456&wantedDids=did:plc:example123" 50 + assert url 51 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedDids=did:plc:example456&wantedDids=did:plc:example123&compress=false&requireHello=false" 47 52 } 48 53 49 54 // Test build_url with both collections and DIDs 50 55 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 - ) 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 + ) 60 66 let url = goose.build_url(config) 61 67 62 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123" 68 + assert url 69 + == "wss://jetstream2.us-east.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&wantedDids=did:plc:example123&compress=false&requireHello=false" 63 70 } 64 71 65 72 // Test build_url with cursor 66 73 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 - ) 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 + ) 76 84 let url = goose.build_url(config) 77 85 78 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?cursor=1234567890123456" 86 + assert url 87 + == "wss://jetstream2.us-east.bsky.network/subscribe?cursor=1234567890123456&compress=false&requireHello=false" 79 88 } 80 89 81 90 // Test build_url with max_message_size_bytes 82 91 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 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 + ) 92 102 let url = goose.build_url(config) 93 103 94 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?maxMessageSizeBytes=1048576" 104 + assert url 105 + == "wss://jetstream2.us-east.bsky.network/subscribe?maxMessageSizeBytes=1048576&compress=false&requireHello=false" 95 106 } 96 107 97 108 // Test build_url with compress enabled 98 109 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 - ) 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 + ) 108 120 let url = goose.build_url(config) 109 121 110 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?compress=true" 122 + assert url 123 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=true&requireHello=false" 111 124 } 112 125 113 126 // Test build_url with require_hello enabled 114 127 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 - ) 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 + ) 124 138 let url = goose.build_url(config) 125 139 126 - assert url == "wss://jetstream2.us-east.bsky.network/subscribe?requireHello=true" 140 + assert url 141 + == "wss://jetstream2.us-east.bsky.network/subscribe?compress=false&requireHello=true" 127 142 } 128 143 129 144 // Test build_url with all options combined 130 145 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 - ) 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 + ) 140 156 let url = goose.build_url(config) 141 157 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" 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" 143 160 } 144 161 145 162 // Test parsing a commit event (create operation with record) 146 163 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\"}}" 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\"}}" 148 166 149 167 let event = goose.parse_event(json) 150 168 151 169 case event { 152 170 goose.CommitEvent(did, time_us, commit) -> { 153 171 assert did == "did:plc:test123" 154 - assert time_us == 1234567890 172 + assert time_us == 1_234_567_890 155 173 assert commit.rev == "abc123" 156 174 assert commit.operation == "create" 157 175 assert commit.collection == "app.bsky.feed.post" ··· 163 181 164 182 // Test parsing a commit event (delete operation without record) 165 183 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\"}}" 184 + let json = 185 + "{\"did\":\"did:plc:test456\",\"time_us\":9876543210,\"commit\":{\"rev\":\"xyz789\",\"operation\":\"delete\",\"collection\":\"app.bsky.feed.like\",\"rkey\":\"like456\"}}" 167 186 168 187 let event = goose.parse_event(json) 169 188 170 189 case event { 171 190 goose.CommitEvent(did, time_us, commit) -> { 172 191 assert did == "did:plc:test456" 173 - assert time_us == 9876543210 192 + assert time_us == 9_876_543_210 174 193 assert commit.rev == "xyz789" 175 194 assert commit.operation == "delete" 176 195 assert commit.collection == "app.bsky.feed.like" ··· 182 201 183 202 // Test parsing an identity event 184 203 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\"}}" 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\"}}" 186 206 187 207 let event = goose.parse_event(json) 188 208 189 209 case event { 190 210 goose.IdentityEvent(did, time_us, identity) -> { 191 211 assert did == "did:plc:identity123" 192 - assert time_us == 1111111111 212 + assert time_us == 1_111_111_111 193 213 assert identity.did == "did:plc:identity123" 194 214 assert identity.handle == "alice.bsky.social" 195 215 assert identity.seq == 42 ··· 201 221 202 222 // Test parsing an account event 203 223 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\"}}" 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\"}}" 205 226 206 227 let event = goose.parse_event(json) 207 228 208 229 case event { 209 230 goose.AccountEvent(did, time_us, account) -> { 210 231 assert did == "did:plc:account789" 211 - assert time_us == 2222222222 232 + assert time_us == 2_222_222_222 212 233 assert account.active == True 213 234 assert account.did == "did:plc:account789" 214 235 assert account.seq == 99