···11+describe DIDKit::Resolver do
22+ let(:sample_did) { 'did:plc:qhfo22pezo44fa3243z2h4ny' }
33+44+ describe '#resolve_handle' do
55+ context 'when handle resolves via HTTP' do
66+ before do
77+ Resolv::DNS.stubs(:open).returns([])
88+ end
99+1010+ let(:handle) { 'barackobama.bsky.social' }
1111+1212+ it 'should return a matching DID' do
1313+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
1414+ .to_return(body: sample_did)
1515+1616+ result = subject.resolve_handle(handle)
1717+1818+ result.should_not be_nil
1919+ result.should be_a(DID)
2020+ result.to_s.should == sample_did
2121+ result.resolved_by.should == :http
2222+ end
2323+2424+ it 'should check DNS first' do
2525+ Resolv::DNS.expects(:open).returns([])
2626+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
2727+ .to_return(body: sample_did)
2828+2929+ result = subject.resolve_handle(handle)
3030+ end
3131+3232+ context 'when HTTP returns invalid text' do
3333+ it 'should return nil' do
3434+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
3535+ .to_return(body: "Welcome to nginx!")
3636+3737+ result = subject.resolve_handle(handle)
3838+ result.should be_nil
3939+ end
4040+ end
4141+4242+ context 'when HTTP returns bad response' do
4343+ it 'should return nil' do
4444+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
4545+ .to_return(status: 400, body: sample_did)
4646+4747+ result = subject.resolve_handle(handle)
4848+ result.should be_nil
4949+ end
5050+ end
5151+5252+ context 'when HTTP throws an exception' do
5353+ it 'should catch it and return nil' do
5454+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
5555+ .to_raise(Errno::ETIMEDOUT)
5656+5757+ result = 0
5858+5959+ expect {
6060+ result = subject.resolve_handle(handle)
6161+ }.to_not raise_error
6262+6363+ result.should be_nil
6464+ end
6565+ end
6666+6767+ context 'when HTTP response has a trailing newline' do
6868+ it 'should accept it' do
6969+ stub_request(:get, "https://#{handle}/.well-known/atproto-did")
7070+ .to_return(body: sample_did + "\n")
7171+7272+ result = subject.resolve_handle(handle)
7373+7474+ result.should_not be_nil
7575+ result.should be_a(DID)
7676+ result.to_s.should == sample_did
7777+ end
7878+ end
7979+ end
8080+8181+ context 'when handle has a leading @' do
8282+ let(:handle) { '@pfrazee.com' }
8383+8484+ before do
8585+ Resolv::DNS.stubs(:open).returns([])
8686+ end
8787+8888+ it 'should also return a matching DID' do
8989+ stub_request(:get, "https://pfrazee.com/.well-known/atproto-did")
9090+ .to_return(body: sample_did)
9191+9292+ result = subject.resolve_handle(handle)
9393+9494+ result.should_not be_nil
9595+ result.should be_a(DID)
9696+ result.to_s.should == sample_did
9797+ result.resolved_by.should == :http
9898+ end
9999+ end
100100+101101+ context 'when handle has a reserved TLD' do
102102+ let(:handle) { 'example.test' }
103103+104104+ it 'should return nil' do
105105+ subject.resolve_handle(handle).should be_nil
106106+ end
107107+ end
108108+109109+ context 'when a DID string is passed' do
110110+ let(:handle) { BSKY_APP_DID }
111111+112112+ it 'should return that DID' do
113113+ result = subject.resolve_handle(handle)
114114+115115+ result.should be_a(DID)
116116+ result.to_s.should == BSKY_APP_DID
117117+ end
118118+ end
119119+120120+ context 'when a DID object is passed' do
121121+ let(:handle) { DID.new(BSKY_APP_DID) }
122122+123123+ it 'should return a new DID object with that DID' do
124124+ result = subject.resolve_handle(handle)
125125+126126+ result.should be_a(DID)
127127+ result.to_s.should == BSKY_APP_DID
128128+ result.equal?(handle).should == false
129129+ end
130130+ end
131131+ end
132132+133133+ describe '#resolve_did' do
134134+ context 'when passed a did:plc string' do
135135+ let(:did) { 'did:plc:yk4dd2qkboz2yv6tpubpc6co' }
136136+137137+ it 'should return a parsed DID document object' do
138138+ stub_request(:get, "https://plc.directory/#{did}")
139139+ .to_return(body: load_did_file('dholms.json'), headers: { 'Content-Type': 'application/did+ld+json; charset=utf-8' })
140140+141141+ result = subject.resolve_did(did)
142142+ result.should be_a(DIDKit::Document)
143143+ result.handles.should == ['dholms.xyz']
144144+ result.pds_endpoint.should == 'https://pds.dholms.xyz'
145145+ end
146146+147147+ it 'should require a valid content type' do
148148+ stub_request(:get, "https://plc.directory/#{did}")
149149+ .to_return(body: load_did_file('dholms.json'), headers: { 'Content-Type': 'text/plain' })
150150+151151+ expect { subject.resolve_did(did) }.to raise_error(DIDKit::APIError)
152152+ end
153153+ end
154154+155155+ context 'when passed a did:web string' do
156156+ let(:did) { 'did:web:witchcraft.systems' }
157157+158158+ it 'should return a parsed DID document object' do
159159+ stub_request(:get, "https://witchcraft.systems/.well-known/did.json")
160160+ .to_return(body: load_did_file('witchcraft.json'), headers: { 'Content-Type': 'application/did+ld+json; charset=utf-8' })
161161+162162+ result = subject.resolve_did(did)
163163+ result.should be_a(DIDKit::Document)
164164+ result.handles.should == ['witchcraft.systems']
165165+ result.pds_endpoint.should == 'https://pds.witchcraft.systems'
166166+ end
167167+168168+ it 'should NOT require a valid content type' do
169169+ stub_request(:get, "https://witchcraft.systems/.well-known/did.json")
170170+ .to_return(body: load_did_file('witchcraft.json'), headers: { 'Content-Type': 'text/plain' })
171171+172172+ result = subject.resolve_did(did)
173173+ result.should be_a(DIDKit::Document)
174174+ result.handles.should == ['witchcraft.systems']
175175+ result.pds_endpoint.should == 'https://pds.witchcraft.systems'
176176+ end
177177+ end
178178+ end
179179+end
+13-5
spec/spec_helper.rb
···11# frozen_string_literal: true
2233-require "didkit"
33+require 'didkit'
44+require 'webmock/rspec'
4556RSpec.configure do |config|
67 # Enable flags like --only-failures and --next-failure
78 config.example_status_persistence_file_path = ".rspec_status"
8999- # Disable RSpec exposing methods globally on `Module` and `main`
1010- config.disable_monkey_patching!
1111-1210 config.expect_with :rspec do |c|
1313- c.syntax = :expect
1111+ c.syntax = [:should, :expect]
1412 end
1313+1414+ config.mock_with :mocha
1515+end
1616+1717+BSKY_APP_DID = 'did:plc:z72i7hdynmk6r22z27h6tvur'
1818+1919+WebMock.enable!
2020+2121+def load_did_file(name)
2222+ File.read(File.join(__dir__, 'dids', name))
1523end