master
go 228 lines 9.04 KB
Raw
1 package core
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 context "context"
9
10 "github.com/ipfs/kubo/repo"
11
12 "github.com/ipfs/boxo/filestore"
13 "github.com/ipfs/boxo/keystore"
14 datastore "github.com/ipfs/go-datastore"
15 syncds "github.com/ipfs/go-datastore/sync"
16 config "github.com/ipfs/kubo/config"
17 "github.com/ipfs/kubo/core/node/libp2p"
18 golib "github.com/libp2p/go-libp2p"
19 ddht "github.com/libp2p/go-libp2p-kad-dht/dual"
20 "github.com/libp2p/go-libp2p-kad-dht/fullrt"
21 routinghelpers "github.com/libp2p/go-libp2p-routing-helpers"
22 "github.com/libp2p/go-libp2p/core/host"
23 "github.com/libp2p/go-libp2p/core/peer"
24 pstore "github.com/libp2p/go-libp2p/core/peerstore"
25 mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
26 )
27
28 func TestInitialization(t *testing.T) {
29 ctx := context.Background()
30 id := testIdentity
31
32 good := []*config.Config{
33 {
34 Identity: id,
35 Addresses: config.Addresses{
36 Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"},
37 API: []string{"/ip4/127.0.0.1/tcp/8000"},
38 },
39 },
40
41 {
42 Identity: id,
43 Addresses: config.Addresses{
44 Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic-v1"},
45 API: []string{"/ip4/127.0.0.1/tcp/8000"},
46 },
47 },
48 }
49
50 bad := []*config.Config{
51 {},
52 }
53
54 for i, c := range good {
55 r := &repo.Mock{
56 C: *c,
57 D: syncds.MutexWrap(datastore.NewMapDatastore()),
58 }
59 n, err := NewNode(ctx, &BuildCfg{Repo: r})
60 if n == nil || err != nil {
61 t.Error("Should have constructed.", i, err)
62 }
63 }
64
65 for i, c := range bad {
66 r := &repo.Mock{
67 C: *c,
68 D: syncds.MutexWrap(datastore.NewMapDatastore()),
69 }
70 n, err := NewNode(ctx, &BuildCfg{Repo: r})
71 if n != nil || err == nil {
72 t.Error("Should have failed to construct.", i)
73 }
74 }
75 }
76
77 var testIdentity = config.Identity{
78 PeerID: "QmNgdzLieYi8tgfo2WfTUzNVH5hQK9oAYGVf6dxN12NrHt",
79 PrivKey: "CAASrRIwggkpAgEAAoICAQCwt67GTUQ8nlJhks6CgbLKOx7F5tl1r9zF4m3TUrG3Pe8h64vi+ILDRFd7QJxaJ/n8ux9RUDoxLjzftL4uTdtv5UXl2vaufCc/C0bhCRvDhuWPhVsD75/DZPbwLsepxocwVWTyq7/ZHsCfuWdoh/KNczfy+Gn33gVQbHCnip/uhTVxT7ARTiv8Qa3d7qmmxsR+1zdL/IRO0mic/iojcb3Oc/PRnYBTiAZFbZdUEit/99tnfSjMDg02wRayZaT5ikxa6gBTMZ16Yvienq7RwSELzMQq2jFA4i/TdiGhS9uKywltiN2LrNDBcQJSN02pK12DKoiIy+wuOCRgs2NTQEhU2sXCk091v7giTTOpFX2ij9ghmiRfoSiBFPJA5RGwiH6ansCHtWKY1K8BS5UORM0o3dYk87mTnKbCsdz4bYnGtOWafujYwzueGx8r+IWiys80IPQKDeehnLW6RgoyjszKgL/2XTyP54xMLSW+Qb3BPgDcPaPO0hmop1hW9upStxKsefW2A2d46Ds4HEpJEry7PkS5M4gKL/zCKHuxuXVk14+fZQ1rstMuvKjrekpAC2aVIKMI9VRA3awtnje8HImQMdj+r+bPmv0N8rTTr3eS4J8Yl7k12i95LLfK+fWnmUh22oTNzkRlaiERQrUDyE4XNCtJc0xs1oe1yXGqazCIAQIDAQABAoICAQCk1N/ftahlRmOfAXk//8wNl7FvdJD3le6+YSKBj0uWmN1ZbUSQk64chr12iGCOM2WY180xYjy1LOS44PTXaeW5bEiTSnb3b3SH+HPHaWCNM2EiSogHltYVQjKW+3tfH39vlOdQ9uQ+l9Gh6iTLOqsCRyszpYPqIBwi1NMLY2Ej8PpVU7ftnFWouHZ9YKS7nAEiMoowhTu/7cCIVwZlAy3AySTuKxPMVj9LORqC32PVvBHZaMPJ+X1Xyijqg6aq39WyoztkXg3+Xxx5j5eOrK6vO/Lp6ZUxaQilHDXoJkKEJjgIBDZpluss08UPfOgiWAGkW+L4fgUxY0qDLDAEMhyEBAn6KOKVL1JhGTX6GjhWziI94bddSpHKYOEIDzUy4H8BXnKhtnyQV6ELS65C2hj9D0IMBTj7edCF1poJy0QfdK0cuXgMvxHLeUO5uc2YWfbNosvKxqygB9rToy4b22YvNwsZUXsTY6Jt+p9V2OgXSKfB5VPeRbjTJL6xqvvUJpQytmII/C9JmSDUtCbYceHj6X9jgigLk20VV6nWHqCTj3utXD6NPAjoycVpLKDlnWEgfVELDIk0gobxUqqSm3jTPEKRPJgxkgPxbwxYumtw++1UY2y35w3WRDc2xYPaWKBCQeZy+mL6ByXp9bWlNvxS3Knb6oZp36/ovGnf2pGvdQKCAQEAyKpipz2lIUySDyE0avVWAmQb2tWGKXALPohzj7AwkcfEg2GuwoC6GyVE2sTJD1HRazIjOKn3yQORg2uOPeG7sx7EKHxSxCKDrbPawkvLCq8JYSy9TLvhqKUVVGYPqMBzu2POSLEA81QXas+aYjKOFWA2Zrjq26zV9ey3+6Lc6WULePgRQybU8+RHJc6fdjUCCfUxgOrUO2IQOuTJ+FsDpVnrMUGlokmWn23OjL4qTL9wGDnWGUs2pjSzNbj3qA0d8iqaiMUyHX/D/VS0wpeT1osNBSm8suvSibYBn+7wbIApbwXUxZaxMv2OHGz3empae4ckvNZs7r8wsI9UwFt8mwKCAQEA4XK6gZkv9t+3YCcSPw2ensLvL/xU7i2bkC9tfTGdjnQfzZXIf5KNdVuj/SerOl2S1s45NMs3ysJbADwRb4ahElD/V71nGzV8fpFTitC20ro9fuX4J0+twmBolHqeH9pmeGTjAeL1rvt6vxs4FkeG/yNft7GdXpXTtEGaObn8Mt0tPY+aB3UnKrnCQoQAlPyGHFrVRX0UEcp6wyyNGhJCNKeNOvqCHTFObhbhO+KWpWSN0MkVHnqaIBnIn1Te8FtvP/iTwXGnKc0YXJUG6+LM6LmOguW6tg8ZqiQeYyyR+e9eCFH4csLzkrTl1GxCxwEsoSLIMm7UDcjttW6tYEghkwKCAQEAmeCO5lCPYImnN5Lu71ZTLmI2OgmjaANTnBBnDbi+hgv61gUCToUIMejSdDCTPfwv61P3TmyIZs0luPGxkiKYHTNqmOE9Vspgz8Mr7fLRMNApESuNvloVIY32XVImj/GEzh4rAfM6F15U1sN8T/EUo6+0B/Glp+9R49QzAfRSE2g48/rGwgf1JVHYfVWFUtAzUA+GdqWdOixo5cCsYJbqpNHfWVZN/bUQnBFIYwUwysnC29D+LUdQEQQ4qOm+gFAOtrWU62zMkXJ4iLt8Ify6kbrvsRXgbhQIzzGS7WH9XDarj0eZciuslr15TLMC1Azadf+cXHLR9gMHA13mT9vYIQKCAQA/DjGv8cKCkAvf7s2hqROGYAs6Jp8yhrsN1tYOwAPLRhtnCs+rLrg17M2vDptLlcRuI/vIElamdTmylRpjUQpX7yObzLO73nfVhpwRJVMdGU394iBIDncQ+JoHfUwgqJskbUM40dvZdyjbrqc/Q/4z+hbZb+oN/GXb8sVKBATPzSDMKQ/xqgisYIw+wmDPStnPsHAaIWOtni47zIgilJzD0WEk78/YjmPbUrboYvWziK5JiRRJFA1rkQqV1c0M+OXixIm+/yS8AksgCeaHr0WUieGcJtjT9uE8vyFop5ykhRiNxy9wGaq6i7IEecsrkd6DqxDHWkwhFuO1bSE83q/VAoIBAEA+RX1i/SUi08p71ggUi9WFMqXmzELp1L3hiEjOc2AklHk2rPxsaTh9+G95BvjhP7fRa/Yga+yDtYuyjO99nedStdNNSg03aPXILl9gs3r2dPiQKUEXZJ3FrH6tkils/8BlpOIRfbkszrdZIKTO9GCdLWQ30dQITDACs8zV/1GFGrHFrqnnMe/NpIFHWNZJ0/WZMi8wgWO6Ik8jHEpQtVXRiXLqy7U6hk170pa4GHOzvftfPElOZZjy9qn7KjdAQqy6spIrAE94OEL+fBgbHQZGLpuTlj6w6YGbMtPU8uo7sXKoc6WOCb68JWft3tejGLDa1946HAWqVM9B/UcneNc=",
80 }
81
82 // mockHostOption creates a HostOption that uses the provided mocknet.
83 // Inlined to avoid import cycle with core/mock package.
84 func mockHostOption(mn mocknet.Mocknet) libp2p.HostOption {
85 return func(id peer.ID, ps pstore.Peerstore, opts ...golib.Option) (host.Host, error) {
86 var cfg golib.Config
87 if err := cfg.Apply(opts...); err != nil {
88 return nil, err
89 }
90
91 // The mocknet does not use the provided libp2p.Option. This options include
92 // the listening addresses we want our peer listening on. Therefore, we have
93 // to manually parse the configuration and add them here.
94 ps.AddAddrs(id, cfg.ListenAddrs, pstore.PermanentAddrTTL)
95 return mn.AddPeerWithPeerstore(id, ps)
96 }
97 }
98
99 func TestHasActiveDHTClient(t *testing.T) {
100 // Test 1: nil DHTClient
101 t.Run("nil DHTClient", func(t *testing.T) {
102 node := &IpfsNode{
103 DHTClient: nil,
104 }
105 if node.HasActiveDHTClient() {
106 t.Error("Expected false for nil DHTClient")
107 }
108 })
109
110 // Test 2: Typed nil *ddht.DHT (common case when Routing.Type=delegated)
111 t.Run("typed nil ddht.DHT", func(t *testing.T) {
112 node := &IpfsNode{
113 DHTClient: (*ddht.DHT)(nil),
114 }
115 if node.HasActiveDHTClient() {
116 t.Error("Expected false for typed nil *ddht.DHT")
117 }
118 })
119
120 // Test 3: Typed nil *fullrt.FullRT (accelerated DHT client)
121 t.Run("typed nil fullrt.FullRT", func(t *testing.T) {
122 node := &IpfsNode{
123 DHTClient: (*fullrt.FullRT)(nil),
124 }
125 if node.HasActiveDHTClient() {
126 t.Error("Expected false for typed nil *fullrt.FullRT")
127 }
128 })
129
130 // Test 4: routinghelpers.Null no-op router (Routing.Type=none)
131 t.Run("routinghelpers.Null", func(t *testing.T) {
132 node := &IpfsNode{
133 DHTClient: routinghelpers.Null{},
134 }
135 if node.HasActiveDHTClient() {
136 t.Error("Expected false for routinghelpers.Null")
137 }
138 })
139
140 // Test 5: Valid standard dual DHT (Routing.Type=auto/dht/dhtclient)
141 t.Run("valid standard dual DHT", func(t *testing.T) {
142 ctx := context.Background()
143 mn := mocknet.New()
144 defer mn.Close()
145
146 ds := syncds.MutexWrap(datastore.NewMapDatastore())
147 c := config.Config{}
148 c.Identity = testIdentity
149 c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001"}
150
151 r := &repo.Mock{
152 C: c,
153 D: ds,
154 K: keystore.NewMemKeystore(),
155 F: filestore.NewFileManager(ds, filepath.Dir(os.TempDir())),
156 }
157
158 node, err := NewNode(ctx, &BuildCfg{
159 Routing: libp2p.DHTServerOption,
160 Repo: r,
161 Host: mockHostOption(mn),
162 Online: true,
163 })
164 if err != nil {
165 t.Fatalf("Failed to create node with DHT: %v", err)
166 }
167 defer node.Close()
168
169 // First verify test setup created the expected DHT type
170 if node.DHTClient == nil {
171 t.Fatalf("Test setup failed: DHTClient is nil")
172 }
173
174 if _, ok := node.DHTClient.(*ddht.DHT); !ok {
175 t.Fatalf("Test setup failed: expected DHTClient to be *ddht.DHT, got %T", node.DHTClient)
176 }
177
178 // Now verify HasActiveDHTClient() correctly identifies it as active
179 if !node.HasActiveDHTClient() {
180 t.Error("Expected true for valid dual DHT client")
181 }
182 })
183
184 // Test 6: Valid accelerated DHT client (Routing.Type=autoclient)
185 t.Run("valid accelerated DHT client", func(t *testing.T) {
186 ctx := context.Background()
187 mn := mocknet.New()
188 defer mn.Close()
189
190 ds := syncds.MutexWrap(datastore.NewMapDatastore())
191 c := config.Config{}
192 c.Identity = testIdentity
193 c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001"}
194 c.Routing.AcceleratedDHTClient = config.True
195
196 r := &repo.Mock{
197 C: c,
198 D: ds,
199 K: keystore.NewMemKeystore(),
200 F: filestore.NewFileManager(ds, filepath.Dir(os.TempDir())),
201 }
202
203 node, err := NewNode(ctx, &BuildCfg{
204 Routing: libp2p.DHTOption,
205 Repo: r,
206 Host: mockHostOption(mn),
207 Online: true,
208 })
209 if err != nil {
210 t.Fatalf("Failed to create node with accelerated DHT: %v", err)
211 }
212 defer node.Close()
213
214 // First verify test setup created the expected accelerated DHT type
215 if node.DHTClient == nil {
216 t.Fatalf("Test setup failed: DHTClient is nil")
217 }
218
219 if _, ok := node.DHTClient.(*fullrt.FullRT); !ok {
220 t.Fatalf("Test setup failed: expected DHTClient to be *fullrt.FullRT, got %T", node.DHTClient)
221 }
222
223 // Now verify HasActiveDHTClient() correctly identifies it as active
224 if !node.HasActiveDHTClient() {
225 t.Error("Expected true for valid accelerated DHT client")
226 }
227 })
228 }