A library for handling DID identifiers used in Bluesky AT Protocol
at master 179 lines 5.6 kB view raw
1describe DIDKit::Resolver do 2 let(:sample_did) { 'did:plc:qhfo22pezo44fa3243z2h4ny' } 3 4 describe '#resolve_handle' do 5 context 'when handle resolves via HTTP' do 6 before do 7 Resolv::DNS.stubs(:open).returns([]) 8 end 9 10 let(:handle) { 'barackobama.bsky.social' } 11 12 it 'should return a matching DID' do 13 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 14 .to_return(body: sample_did) 15 16 result = subject.resolve_handle(handle) 17 18 result.should_not be_nil 19 result.should be_a(DID) 20 result.to_s.should == sample_did 21 result.resolved_by.should == :http 22 end 23 24 it 'should check DNS first' do 25 Resolv::DNS.expects(:open).returns([]) 26 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 27 .to_return(body: sample_did) 28 29 result = subject.resolve_handle(handle) 30 end 31 32 context 'when HTTP returns invalid text' do 33 it 'should return nil' do 34 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 35 .to_return(body: "Welcome to nginx!") 36 37 result = subject.resolve_handle(handle) 38 result.should be_nil 39 end 40 end 41 42 context 'when HTTP returns bad response' do 43 it 'should return nil' do 44 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 45 .to_return(status: 400, body: sample_did) 46 47 result = subject.resolve_handle(handle) 48 result.should be_nil 49 end 50 end 51 52 context 'when HTTP throws an exception' do 53 it 'should catch it and return nil' do 54 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 55 .to_raise(Errno::ETIMEDOUT) 56 57 result = 0 58 59 expect { 60 result = subject.resolve_handle(handle) 61 }.to_not raise_error 62 63 result.should be_nil 64 end 65 end 66 67 context 'when HTTP response has a trailing newline' do 68 it 'should accept it' do 69 stub_request(:get, "https://#{handle}/.well-known/atproto-did") 70 .to_return(body: sample_did + "\n") 71 72 result = subject.resolve_handle(handle) 73 74 result.should_not be_nil 75 result.should be_a(DID) 76 result.to_s.should == sample_did 77 end 78 end 79 end 80 81 context 'when handle has a leading @' do 82 let(:handle) { '@pfrazee.com' } 83 84 before do 85 Resolv::DNS.stubs(:open).returns([]) 86 end 87 88 it 'should also return a matching DID' do 89 stub_request(:get, "https://pfrazee.com/.well-known/atproto-did") 90 .to_return(body: sample_did) 91 92 result = subject.resolve_handle(handle) 93 94 result.should_not be_nil 95 result.should be_a(DID) 96 result.to_s.should == sample_did 97 result.resolved_by.should == :http 98 end 99 end 100 101 context 'when handle has a reserved TLD' do 102 let(:handle) { 'example.test' } 103 104 it 'should return nil' do 105 subject.resolve_handle(handle).should be_nil 106 end 107 end 108 109 context 'when a DID string is passed' do 110 let(:handle) { BSKY_APP_DID } 111 112 it 'should return that DID' do 113 result = subject.resolve_handle(handle) 114 115 result.should be_a(DID) 116 result.to_s.should == BSKY_APP_DID 117 end 118 end 119 120 context 'when a DID object is passed' do 121 let(:handle) { DID.new(BSKY_APP_DID) } 122 123 it 'should return a new DID object with that DID' do 124 result = subject.resolve_handle(handle) 125 126 result.should be_a(DID) 127 result.to_s.should == BSKY_APP_DID 128 result.equal?(handle).should == false 129 end 130 end 131 end 132 133 describe '#resolve_did' do 134 context 'when passed a did:plc string' do 135 let(:did) { 'did:plc:yk4dd2qkboz2yv6tpubpc6co' } 136 137 it 'should return a parsed DID document object' do 138 stub_request(:get, "https://plc.directory/#{did}") 139 .to_return(body: load_did_file('dholms.json'), headers: { 'Content-Type': 'application/did+ld+json; charset=utf-8' }) 140 141 result = subject.resolve_did(did) 142 result.should be_a(DIDKit::Document) 143 result.handles.should == ['dholms.xyz'] 144 result.pds_endpoint.should == 'https://pds.dholms.xyz' 145 end 146 147 it 'should require a valid content type' do 148 stub_request(:get, "https://plc.directory/#{did}") 149 .to_return(body: load_did_file('dholms.json'), headers: { 'Content-Type': 'text/plain' }) 150 151 expect { subject.resolve_did(did) }.to raise_error(DIDKit::APIError) 152 end 153 end 154 155 context 'when passed a did:web string' do 156 let(:did) { 'did:web:witchcraft.systems' } 157 158 it 'should return a parsed DID document object' do 159 stub_request(:get, "https://witchcraft.systems/.well-known/did.json") 160 .to_return(body: load_did_file('witchcraft.json'), headers: { 'Content-Type': 'application/did+ld+json; charset=utf-8' }) 161 162 result = subject.resolve_did(did) 163 result.should be_a(DIDKit::Document) 164 result.handles.should == ['witchcraft.systems'] 165 result.pds_endpoint.should == 'https://pds.witchcraft.systems' 166 end 167 168 it 'should NOT require a valid content type' do 169 stub_request(:get, "https://witchcraft.systems/.well-known/did.json") 170 .to_return(body: load_did_file('witchcraft.json'), headers: { 'Content-Type': 'text/plain' }) 171 172 result = subject.resolve_did(did) 173 result.should be_a(DIDKit::Document) 174 result.handles.should == ['witchcraft.systems'] 175 result.pds_endpoint.should == 'https://pds.witchcraft.systems' 176 end 177 end 178 end 179end