A library for handling DID identifiers used in Bluesky AT Protocol

added more specs

+268 -1
+121
spec/did_spec.rb
··· 114 114 did.to_s.should == plc_did 115 115 end 116 116 end 117 + 118 + describe 'account status' do 119 + let(:document) { stub(:pds_endpoint => 'https://pds.ruby.space') } 120 + let(:did) { subject.new(plc_did) } 121 + 122 + before do 123 + did.stubs(:document).returns(document) 124 + 125 + stub_request(:get, 'https://pds.ruby.space/xrpc/com.atproto.sync.getRepoStatus') 126 + .with(query: { did: plc_did }) 127 + .to_return(http_response) if defined?(http_response) 128 + end 129 + 130 + context 'when repo is active' do 131 + let(:http_response) { 132 + { body: { active: true }.to_json, headers: { 'Content-Type' => 'application/json' }} 133 + } 134 + 135 + it 'should report active account state' do 136 + did.account_status.should == :active 137 + did.account_active?.should == true 138 + did.account_exists?.should == true 139 + end 140 + end 141 + 142 + context 'when repo is inactive' do 143 + let(:http_response) { 144 + { body: { active: false, status: 'takendown' }.to_json, headers: { 'Content-Type' => 'application/json' }} 145 + } 146 + 147 + it 'should report an inactive existing account' do 148 + did.account_status.should == :takendown 149 + did.account_active?.should == false 150 + did.account_exists?.should == true 151 + end 152 + end 153 + 154 + context 'when repo is not found' do 155 + let(:http_response) { 156 + { status: 400, body: { error: 'RepoNotFound' }.to_json, headers: { 'Content-Type' => 'application/json' }} 157 + } 158 + 159 + it 'should return nil status and report the account as missing' do 160 + did.account_status.should be_nil 161 + did.account_active?.should == false 162 + did.account_exists?.should == false 163 + end 164 + end 165 + 166 + context 'when the document has no pds endpoint' do 167 + before do 168 + did.stubs(:document).returns(stub(:pds_endpoint => nil)) 169 + end 170 + 171 + it 'should return nil status and report the account as missing' do 172 + did.account_status.should be_nil 173 + did.account_active?.should == false 174 + did.account_exists?.should == false 175 + end 176 + end 177 + 178 + context 'when active field is not set' do 179 + let(:http_response) { 180 + { body: { active: nil, status: 'unknown' }.to_json, headers: { 'Content-Type' => 'application/json' }} 181 + } 182 + 183 + it 'should raise APIError' do 184 + expect { did.account_status }.to raise_error(DIDKit::APIError) 185 + expect { did.account_active? }.to raise_error(DIDKit::APIError) 186 + expect { did.account_exists? }.to raise_error(DIDKit::APIError) 187 + end 188 + end 189 + 190 + context 'when active is false but status is not set' do 191 + let(:http_response) { 192 + { body: { active: false, status: nil }.to_json, headers: { 'Content-Type' => 'application/json' }} 193 + } 194 + 195 + it 'should raise APIError' do 196 + expect { did.account_status }.to raise_error(DIDKit::APIError) 197 + expect { did.account_active? }.to raise_error(DIDKit::APIError) 198 + expect { did.account_exists? }.to raise_error(DIDKit::APIError) 199 + end 200 + end 201 + 202 + context 'when an error different than RepoNotFound is returned' do 203 + let(:http_response) { 204 + { status: 400, body: { error: 'UserIsJerry' }.to_json, headers: { 'Content-Type' => 'application/json' }} 205 + } 206 + 207 + it 'should raise APIError' do 208 + expect { did.account_status }.to raise_error(DIDKit::APIError) 209 + expect { did.account_active? }.to raise_error(DIDKit::APIError) 210 + expect { did.account_exists? }.to raise_error(DIDKit::APIError) 211 + end 212 + end 213 + 214 + context 'when the response is not application/json' do 215 + let(:http_response) { 216 + { status: 400, body: 'error', headers: { 'Content-Type' => 'text/html' }} 217 + } 218 + 219 + it 'should raise APIError' do 220 + expect { did.account_status }.to raise_error(DIDKit::APIError) 221 + expect { did.account_active? }.to raise_error(DIDKit::APIError) 222 + expect { did.account_exists? }.to raise_error(DIDKit::APIError) 223 + end 224 + end 225 + 226 + context 'when the response is not 200 or 400' do 227 + let(:http_response) { 228 + { status: 500, body: { error: 'RepoNotFound' }.to_json, headers: { 'Content-Type' => 'application/json' }} 229 + } 230 + 231 + it 'should raise APIError' do 232 + expect { did.account_status }.to raise_error(DIDKit::APIError) 233 + expect { did.account_active? }.to raise_error(DIDKit::APIError) 234 + expect { did.account_exists? }.to raise_error(DIDKit::APIError) 235 + end 236 + end 237 + end 117 238 end
+1 -1
spec/document_spec.rb
··· 174 174 end 175 175 end 176 176 177 - describe '#pds_host' do 177 + describe '#labeler_host' do 178 178 it 'should return the host part of #atproto_labeler endpoint' do 179 179 doc = subject.new(did, service_json) 180 180 doc.labeler_host.should == 'labels.dholms.xyz'
+146
spec/plc_operation_spec.rb
··· 208 208 expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError) 209 209 end 210 210 end 211 + 212 + context 'when a service entry is missing fields' do 213 + let(:json) { 214 + base_json.tap { |h| 215 + h['operation']['services'] = { 216 + "atproto_pds" => { 217 + "endpoint" => "https://pds.dholms.xyz" 218 + }, 219 + "atproto_labeler" => { 220 + "type" => "AtprotoLabeler", 221 + "endpoint" => "https://labeler.example.com" 222 + } 223 + } 224 + } 225 + } 226 + 227 + it 'should raise a format error' do 228 + expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError) 229 + end 230 + end 231 + 232 + context 'when services are valid' do 233 + let(:json) { 234 + base_json.tap { |h| 235 + h['operation']['services'] = { 236 + "atproto_pds" => { 237 + "type" => "AtprotoPersonalDataServer", 238 + "endpoint" => "https://pds.dholms.xyz" 239 + }, 240 + "atproto_labeler" => { 241 + "type" => "AtprotoLabeler", 242 + "endpoint" => "https://labeler.example.com" 243 + }, 244 + "custom_service" => { 245 + "type" => "OtherService", 246 + "endpoint" => "https://custom.example.com" 247 + } 248 + } 249 + } 250 + } 251 + 252 + it 'should parse services into ServiceRecords' do 253 + op = subject.new(json) 254 + 255 + op.services.length.should == 3 256 + op.services.each { |s| s.should be_a(DIDKit::ServiceRecord) } 257 + 258 + pds, labeller, custom = op.services 259 + 260 + pds.type.should == 'AtprotoPersonalDataServer' 261 + pds.endpoint.should == 'https://pds.dholms.xyz' 262 + 263 + labeller.type.should == 'AtprotoLabeler' 264 + labeller.endpoint.should == 'https://labeler.example.com' 265 + 266 + custom.type.should == 'OtherService' 267 + custom.endpoint.should == 'https://custom.example.com' 268 + end 269 + 270 + it 'should allow fetching services by key + type' do 271 + op = subject.new(json) 272 + 273 + custom = op.get_service('custom_service', 'OtherService') 274 + custom.should be_a(DIDKit::ServiceRecord) 275 + custom.endpoint.should == 'https://custom.example.com' 276 + end 277 + 278 + describe '#pds_endpoint' do 279 + it 'should return the endpoint of #atproto_pds' do 280 + op = subject.new(json) 281 + op.pds_endpoint.should == 'https://pds.dholms.xyz' 282 + end 283 + end 284 + 285 + describe '#pds_host' do 286 + it 'should return the host part of #atproto_pds endpoint' do 287 + op = subject.new(json) 288 + op.pds_host.should == 'pds.dholms.xyz' 289 + end 290 + end 291 + 292 + describe '#labeler_endpoint' do 293 + it 'should return the endpoint of #atproto_labeler' do 294 + op = subject.new(json) 295 + op.labeler_endpoint.should == 'https://labeler.example.com' 296 + end 297 + end 298 + 299 + describe '#labeler_host' do 300 + it 'should return the host part of #atproto_labeler endpoint' do 301 + op = subject.new(json) 302 + op.labeler_host.should == 'labeler.example.com' 303 + end 304 + end 305 + 306 + it 'should expose the "labeller" aliases for endpoint and host' do 307 + op = subject.new(json) 308 + 309 + op.labeller_endpoint.should == 'https://labeler.example.com' 310 + op.labeller_host.should == 'labeler.example.com' 311 + end 312 + end 313 + 314 + context 'when services are valid but the specific ones are missing' do 315 + let(:json) { 316 + base_json.tap { |h| 317 + h['operation']['services'] = { 318 + "custom_service" => { 319 + "type" => "CustomService", 320 + "endpoint" => "https://custom.example.com" 321 + } 322 + } 323 + } 324 + } 325 + 326 + it 'should parse service records' do 327 + op = subject.new(json) 328 + op.services.length.should == 1 329 + end 330 + 331 + describe '#get_service' do 332 + it 'should return nil' do 333 + op = subject.new(json) 334 + other = op.get_service('other_service', 'OtherService') 335 + other.should be_nil 336 + end 337 + end 338 + 339 + describe '#pds_endpoint' do 340 + it 'should return nil' do 341 + op = subject.new(json) 342 + op.pds_endpoint.should be_nil 343 + op.pds_host.should be_nil 344 + end 345 + end 346 + 347 + describe '#labeler_endpoint' do 348 + it 'should return nil' do 349 + op = subject.new(json) 350 + op.labeler_endpoint.should be_nil 351 + op.labeller_endpoint.should be_nil 352 + op.labeler_host.should be_nil 353 + op.labeller_host.should be_nil 354 + end 355 + end 356 + end 211 357 end 212 358 end