A library for handling DID identifiers used in Bluesky AT Protocol
at master 233 lines 7.4 kB view raw
1describe DIDKit::Document do 2 subject { described_class } 3 4 let(:did) { DID.new('did:plc:yk4dd2qkboz2yv6tpubpc6co') } 5 let(:base_json) { load_did_json('dholms.json') } 6 7 describe '#initialize' do 8 context 'with valid input' do 9 let(:json) { base_json } 10 11 it 'should return a Document object' do 12 doc = subject.new(did, json) 13 14 doc.should be_a(DIDKit::Document) 15 doc.did.should == did 16 doc.json.should == json 17 end 18 19 it 'should parse services from the JSON' do 20 doc = subject.new(did, json) 21 22 doc.services.should be_an(Array) 23 doc.services.length.should == 1 24 25 doc.services[0].should be_a(DIDKit::ServiceRecord) 26 doc.services[0].key.should == 'atproto_pds' 27 doc.services[0].type.should == 'AtprotoPersonalDataServer' 28 doc.services[0].endpoint.should == 'https://pds.dholms.xyz' 29 end 30 31 it 'should parse handles from the JSON' do 32 doc = subject.new(did, json) 33 34 doc.handles.should == ['dholms.xyz'] 35 end 36 end 37 38 context 'when id is missing' do 39 let(:json) { base_json.dup.tap { |h| h.delete('id') }} 40 41 it 'should raise a format error' do 42 expect { 43 subject.new(did, json) 44 }.to raise_error(DIDKit::FormatError) 45 end 46 end 47 48 context 'when id is not a string' do 49 let(:json) { base_json.merge('id' => 123) } 50 51 it 'should raise a format error' do 52 expect { 53 subject.new(did, json) 54 }.to raise_error(DIDKit::FormatError) 55 end 56 end 57 58 context 'when id does not match the DID' do 59 let(:json) { base_json.merge('id' => 'did:plc:notmatching') } 60 61 it 'should raise a format error' do 62 expect { 63 subject.new(did, json) 64 }.to raise_error(DIDKit::FormatError) 65 end 66 end 67 68 context 'when alsoKnownAs is not an array' do 69 let(:json) { base_json.merge('alsoKnownAs' => 'at://dholms.xyz') } 70 71 it 'should raise an AtHandles format error' do 72 expect { 73 subject.new(did, json) 74 }.to raise_error(DIDKit::FormatError) 75 end 76 end 77 78 context 'when alsoKnownAs elements are not strings' do 79 let(:json) { base_json.merge('alsoKnownAs' => [666]) } 80 81 it 'should raise an AtHandles format error' do 82 expect { 83 subject.new(did, json) 84 }.to raise_error(DIDKit::FormatError) 85 end 86 end 87 88 context 'when alsoKnownAs contains multiple handles' do 89 let(:json) { 90 base_json.merge('alsoKnownAs' => [ 91 'at://dholms.xyz', 92 'https://example.com', 93 'at://other.handle' 94 ]) 95 } 96 97 it 'should pick those starting with at:// and remove the prefixes' do 98 doc = subject.new(did, json) 99 doc.handles.should == ['dholms.xyz', 'other.handle'] 100 end 101 end 102 103 context 'when service is not an array' do 104 let(:json) { base_json.merge('service' => 'not-an-array') } 105 106 it 'should raise a format error' do 107 expect { 108 subject.new(did, json) 109 }.to raise_error(DIDKit::FormatError) 110 end 111 end 112 113 context 'when service entries are not hashes' do 114 let(:json) { base_json.merge('service' => ['invalid']) } 115 116 it 'should raise a format error' do 117 expect { 118 subject.new(did, json) 119 }.to raise_error(DIDKit::FormatError) 120 end 121 end 122 123 context 'when service entries are partially valid' do 124 let(:services) { 125 [ 126 { 'id' => '#atproto_pds', 'type' => 'AtprotoPersonalDataServer', 'serviceEndpoint' => 'https://pds.dholms.xyz' }, 127 { 'id' => 'not_a_hash', 'type' => 'AtprotoPersonalDataServer', 'serviceEndpoint' => 'https://pds.dholms.xyz' }, 128 { 'id' => '#wrong_type', 'type' => 123, 'serviceEndpoint' => 'https://pds.dholms.xyz' }, 129 { 'id' => '#wrong_endpoint', 'type' => 'AtprotoPersonalDataServer', 'serviceEndpoint' => 123 }, 130 { 'id' => '#lycan', 'type' => 'LycanService', 'serviceEndpoint' => 'https://lycan.feeds.blue' } 131 ] 132 } 133 134 let(:json) { base_json.merge('service' => services) } 135 136 it 'should only keep the valid records' do 137 doc = subject.new(did, json) 138 139 doc.services.length.should == 2 140 doc.services.map(&:key).should == ['atproto_pds', 'lycan'] 141 doc.services.map(&:type).should == ['AtprotoPersonalDataServer', 'LycanService'] 142 doc.services.map(&:endpoint).should == ['https://pds.dholms.xyz', 'https://lycan.feeds.blue'] 143 end 144 end 145 end 146 147 describe 'service helpers' do 148 let(:service_json) { 149 base_json.merge('service' => [ 150 { 'id' => '#atproto_pds', 'type' => 'AtprotoPersonalDataServer', 'serviceEndpoint' => 'https://pds.dholms.xyz' }, 151 { 'id' => '#atproto_labeler', 'type' => 'AtprotoLabeler', 'serviceEndpoint' => 'https://labels.dholms.xyz' }, 152 { 'id' => '#lycan', 'type' => 'LycanService', 'serviceEndpoint' => 'https://lycan.feeds.blue' } 153 ]) 154 } 155 156 describe '#pds_endpoint' do 157 it 'should return the endpoint of #atproto_pds' do 158 doc = subject.new(did, service_json) 159 doc.pds_endpoint.should == 'https://pds.dholms.xyz' 160 end 161 end 162 163 describe '#pds_host' do 164 it 'should return the host part of #atproto_pds endpoint' do 165 doc = subject.new(did, service_json) 166 doc.pds_host.should == 'pds.dholms.xyz' 167 end 168 end 169 170 describe '#labeler_endpoint' do 171 it 'should return the endpoint of #atproto_labeler' do 172 doc = subject.new(did, service_json) 173 doc.labeler_endpoint.should == 'https://labels.dholms.xyz' 174 end 175 end 176 177 describe '#labeler_host' do 178 it 'should return the host part of #atproto_labeler endpoint' do 179 doc = subject.new(did, service_json) 180 doc.labeler_host.should == 'labels.dholms.xyz' 181 end 182 end 183 184 describe '#get_service' do 185 it 'should fetch a service by key and type' do 186 doc = subject.new(did, service_json) 187 188 lycan = doc.get_service('lycan', 'LycanService') 189 lycan.should_not be_nil 190 lycan.endpoint.should == 'https://lycan.feeds.blue' 191 end 192 193 it 'should return nil if none of the services match' do 194 doc = subject.new(did, service_json) 195 196 result = doc.get_service('lycan', 'AtprotoLabeler') 197 result.should be_nil 198 199 result = doc.get_service('atproto_pds', 'PDS') 200 result.should be_nil 201 202 result = doc.get_service('unknown', 'Test') 203 result.should be_nil 204 end 205 end 206 207 it 'should expose the "labeller" aliases for endpoint and host' do 208 doc = subject.new(did, service_json) 209 210 doc.labeller_endpoint.should == 'https://labels.dholms.xyz' 211 doc.labeller_host.should == 'labels.dholms.xyz' 212 end 213 214 describe 'if there is no matching service' do 215 let(:service_json) { 216 base_json.merge('service' => [ 217 { 'id' => '#lycan', 'type' => 'LycanService', 'serviceEndpoint' => 'https://lycan.feeds.blue' } 218 ]) 219 } 220 221 it 'should return nil from the relevant methods' do 222 doc = subject.new(did, service_json) 223 224 doc.pds_endpoint.should be_nil 225 doc.pds_host.should be_nil 226 doc.labeller_endpoint.should be_nil 227 doc.labeller_host.should be_nil 228 doc.labeler_endpoint.should be_nil 229 doc.labeler_host.should be_nil 230 end 231 end 232 end 233end