From f84653ad57cf1dfacdae634ce947cd90f1265fb3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Wed, 29 Nov 2023 10:20:33 +0100 Subject: [PATCH] coreiface: remove and move to Kubo --- CHANGELOG.md | 1 + coreiface/block.go | 37 -- coreiface/coreapi.go | 61 -- coreiface/dag.go | 13 - coreiface/dht.go | 27 - coreiface/errors.go | 10 - coreiface/idfmt.go | 19 - coreiface/key.go | 43 -- coreiface/name.go | 40 -- coreiface/object.go | 107 ---- coreiface/options/block.go | 165 ------ coreiface/options/dht.go | 64 -- coreiface/options/global.go | 47 -- coreiface/options/key.go | 89 --- coreiface/options/name.go | 131 ---- coreiface/options/object.go | 126 ---- coreiface/options/pin.go | 283 --------- coreiface/options/pubsub.go | 60 -- coreiface/options/routing.go | 35 -- coreiface/options/unixfs.go | 295 --------- coreiface/pin.go | 66 --- coreiface/pubsub.go | 48 -- coreiface/routing.go | 16 - coreiface/swarm.go | 57 -- coreiface/tests/api.go | 110 ---- coreiface/tests/block.go | 353 ----------- coreiface/tests/dag.go | 199 ------- coreiface/tests/dht.go | 166 ------ coreiface/tests/key.go | 538 ----------------- coreiface/tests/name.go | 168 ------ coreiface/tests/object.go | 467 --------------- coreiface/tests/path.go | 148 ----- coreiface/tests/pin.go | 611 ------------------- coreiface/tests/pubsub.go | 136 ----- coreiface/tests/routing.go | 100 ---- coreiface/tests/unixfs.go | 1084 ---------------------------------- coreiface/unixfs.go | 79 --- coreiface/util.go | 20 - go.mod | 2 +- 39 files changed, 2 insertions(+), 6019 deletions(-) delete mode 100644 coreiface/block.go delete mode 100644 coreiface/coreapi.go delete mode 100644 coreiface/dag.go delete mode 100644 coreiface/dht.go delete mode 100644 coreiface/errors.go delete mode 100644 coreiface/idfmt.go delete mode 100644 coreiface/key.go delete mode 100644 coreiface/name.go delete mode 100644 coreiface/object.go delete mode 100644 coreiface/options/block.go delete mode 100644 coreiface/options/dht.go delete mode 100644 coreiface/options/global.go delete mode 100644 coreiface/options/key.go delete mode 100644 coreiface/options/name.go delete mode 100644 coreiface/options/object.go delete mode 100644 coreiface/options/pin.go delete mode 100644 coreiface/options/pubsub.go delete mode 100644 coreiface/options/routing.go delete mode 100644 coreiface/options/unixfs.go delete mode 100644 coreiface/pin.go delete mode 100644 coreiface/pubsub.go delete mode 100644 coreiface/routing.go delete mode 100644 coreiface/swarm.go delete mode 100644 coreiface/tests/api.go delete mode 100644 coreiface/tests/block.go delete mode 100644 coreiface/tests/dag.go delete mode 100644 coreiface/tests/dht.go delete mode 100644 coreiface/tests/key.go delete mode 100644 coreiface/tests/name.go delete mode 100644 coreiface/tests/object.go delete mode 100644 coreiface/tests/path.go delete mode 100644 coreiface/tests/pin.go delete mode 100644 coreiface/tests/pubsub.go delete mode 100644 coreiface/tests/routing.go delete mode 100644 coreiface/tests/unixfs.go delete mode 100644 coreiface/unixfs.go delete mode 100644 coreiface/util.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 749bbdb06..bffc752a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The following emojis are used to highlight certain changes: * 🛠 `boxo/gateway`: removed support for undocumented legacy `ipfs-404.html`. Use [`_redirects`](https://specs.ipfs.tech/http-gateways/web-redirects-file/) instead. * 🛠 `boxo/namesys`: removed support for legacy DNSLink entries at the root of the domain. Use [`_dnslink.` TXT record](https://docs.ipfs.tech/concepts/dnslink/) instead. +* 🛠 `boxo/coreapi`, an intrinsic part of Kubo, has been removed and moved to `kubo/core/coreiface`. ### Fixed diff --git a/coreiface/block.go b/coreiface/block.go deleted file mode 100644 index cdd5fcee2..000000000 --- a/coreiface/block.go +++ /dev/null @@ -1,37 +0,0 @@ -package iface - -import ( - "context" - "io" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" -) - -// BlockStat contains information about a block -type BlockStat interface { - // Size is the size of a block - Size() int - - // Path returns path to the block - Path() path.ImmutablePath -} - -// BlockAPI specifies the interface to the block layer -type BlockAPI interface { - // Put imports raw block data, hashing it using specified settings. - Put(context.Context, io.Reader, ...options.BlockPutOption) (BlockStat, error) - - // Get attempts to resolve the path and return a reader for data in the block - Get(context.Context, path.Path) (io.Reader, error) - - // Rm removes the block specified by the path from local blockstore. - // By default an error will be returned if the block can't be found locally. - // - // NOTE: If the specified block is pinned it won't be removed and no error - // will be returned - Rm(context.Context, path.Path, ...options.BlockRmOption) error - - // Stat returns information on - Stat(context.Context, path.Path) (BlockStat, error) -} diff --git a/coreiface/coreapi.go b/coreiface/coreapi.go deleted file mode 100644 index 25e54a37b..000000000 --- a/coreiface/coreapi.go +++ /dev/null @@ -1,61 +0,0 @@ -// Package iface defines IPFS Core API which is a set of interfaces used to -// interact with IPFS nodes. -package iface - -import ( - "context" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - - ipld "github.com/ipfs/go-ipld-format" -) - -// CoreAPI defines an unified interface to IPFS for Go programs -type CoreAPI interface { - // Unixfs returns an implementation of Unixfs API - Unixfs() UnixfsAPI - - // Block returns an implementation of Block API - Block() BlockAPI - - // Dag returns an implementation of Dag API - Dag() APIDagService - - // Name returns an implementation of Name API - Name() NameAPI - - // Key returns an implementation of Key API - Key() KeyAPI - - // Pin returns an implementation of Pin API - Pin() PinAPI - - // Object returns an implementation of Object API - Object() ObjectAPI - - // Dht returns an implementation of Dht API - Dht() DhtAPI - - // Swarm returns an implementation of Swarm API - Swarm() SwarmAPI - - // PubSub returns an implementation of PubSub API - PubSub() PubSubAPI - - // Routing returns an implementation of Routing API - Routing() RoutingAPI - - // ResolvePath resolves the path using UnixFS resolver, and returns the resolved - // immutable path, and the remainder of the path segments that cannot be resolved - // within UnixFS. - ResolvePath(context.Context, path.Path) (path.ImmutablePath, []string, error) - - // ResolveNode resolves the path (if not resolved already) using Unixfs - // resolver, gets and returns the resolved Node - ResolveNode(context.Context, path.Path) (ipld.Node, error) - - // WithOptions creates new instance of CoreAPI based on this instance with - // a set of options applied - WithOptions(...options.ApiOption) (CoreAPI, error) -} diff --git a/coreiface/dag.go b/coreiface/dag.go deleted file mode 100644 index 3cc3aeb4d..000000000 --- a/coreiface/dag.go +++ /dev/null @@ -1,13 +0,0 @@ -package iface - -import ( - ipld "github.com/ipfs/go-ipld-format" -) - -// APIDagService extends ipld.DAGService -type APIDagService interface { - ipld.DAGService - - // Pinning returns special NodeAdder which recursively pins added nodes - Pinning() ipld.NodeAdder -} diff --git a/coreiface/dht.go b/coreiface/dht.go deleted file mode 100644 index d9418ebfc..000000000 --- a/coreiface/dht.go +++ /dev/null @@ -1,27 +0,0 @@ -package iface - -import ( - "context" - - "github.com/ipfs/boxo/path" - - "github.com/ipfs/boxo/coreiface/options" - - "github.com/libp2p/go-libp2p/core/peer" -) - -// DhtAPI specifies the interface to the DHT -// Note: This API will likely get deprecated in near future, see -// https://github.com/ipfs/interface-ipfs-core/issues/249 for more context. -type DhtAPI interface { - // FindPeer queries the DHT for all of the multiaddresses associated with a - // Peer ID - FindPeer(context.Context, peer.ID) (peer.AddrInfo, error) - - // FindProviders finds peers in the DHT who can provide a specific value - // given a key. - FindProviders(context.Context, path.Path, ...options.DhtFindProvidersOption) (<-chan peer.AddrInfo, error) - - // Provide announces to the network that you are providing given values - Provide(context.Context, path.Path, ...options.DhtProvideOption) error -} diff --git a/coreiface/errors.go b/coreiface/errors.go deleted file mode 100644 index e0bd7805d..000000000 --- a/coreiface/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package iface - -import "errors" - -var ( - ErrIsDir = errors.New("this dag node is a directory") - ErrNotFile = errors.New("this dag node is not a regular file") - ErrOffline = errors.New("this action must be run in online mode, try running 'ipfs daemon' first") - ErrNotSupported = errors.New("operation not supported") -) diff --git a/coreiface/idfmt.go b/coreiface/idfmt.go deleted file mode 100644 index 80fd0f822..000000000 --- a/coreiface/idfmt.go +++ /dev/null @@ -1,19 +0,0 @@ -package iface - -import ( - "github.com/libp2p/go-libp2p/core/peer" - mbase "github.com/multiformats/go-multibase" -) - -func FormatKeyID(id peer.ID) string { - if s, err := peer.ToCid(id).StringOfBase(mbase.Base36); err != nil { - panic(err) - } else { - return s - } -} - -// FormatKey formats the given IPNS key in a canonical way. -func FormatKey(key Key) string { - return FormatKeyID(key.ID()) -} diff --git a/coreiface/key.go b/coreiface/key.go deleted file mode 100644 index 4a1cbae80..000000000 --- a/coreiface/key.go +++ /dev/null @@ -1,43 +0,0 @@ -package iface - -import ( - "context" - - "github.com/ipfs/boxo/path" - - "github.com/ipfs/boxo/coreiface/options" - - "github.com/libp2p/go-libp2p/core/peer" -) - -// Key specifies the interface to Keys in KeyAPI Keystore -type Key interface { - // Key returns key name - Name() string - - // Path returns key path - Path() path.Path - - // ID returns key PeerID - ID() peer.ID -} - -// KeyAPI specifies the interface to Keystore -type KeyAPI interface { - // Generate generates new key, stores it in the keystore under the specified - // name and returns a base58 encoded multihash of it's public key - Generate(ctx context.Context, name string, opts ...options.KeyGenerateOption) (Key, error) - - // Rename renames oldName key to newName. Returns the key and whether another - // key was overwritten, or an error - Rename(ctx context.Context, oldName string, newName string, opts ...options.KeyRenameOption) (Key, bool, error) - - // List lists keys stored in keystore - List(ctx context.Context) ([]Key, error) - - // Self returns the 'main' node key - Self(ctx context.Context) (Key, error) - - // Remove removes keys from keystore. Returns ipns path of the removed key - Remove(ctx context.Context, name string) (Key, error) -} diff --git a/coreiface/name.go b/coreiface/name.go deleted file mode 100644 index f832033ef..000000000 --- a/coreiface/name.go +++ /dev/null @@ -1,40 +0,0 @@ -package iface - -import ( - "context" - "errors" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/ipns" - "github.com/ipfs/boxo/path" -) - -var ErrResolveFailed = errors.New("could not resolve name") - -type IpnsResult struct { - path.Path - Err error -} - -// NameAPI specifies the interface to IPNS. -// -// IPNS is a PKI namespace, where names are the hashes of public keys, and the -// private key enables publishing new (signed) values. In both publish and -// resolve, the default name used is the node's own PeerID, which is the hash of -// its public key. -// -// You can use .Key API to list and generate more names and their respective keys. -type NameAPI interface { - // Publish announces new IPNS name - Publish(ctx context.Context, path path.Path, opts ...options.NamePublishOption) (ipns.Name, error) - - // Resolve attempts to resolve the newest version of the specified name - Resolve(ctx context.Context, name string, opts ...options.NameResolveOption) (path.Path, error) - - // Search is a version of Resolve which outputs paths as they are discovered, - // reducing the time to first entry - // - // Note: by default, all paths read from the channel are considered unsafe, - // except the latest (last path in channel read buffer). - Search(ctx context.Context, name string, opts ...options.NameResolveOption) (<-chan IpnsResult, error) -} diff --git a/coreiface/object.go b/coreiface/object.go deleted file mode 100644 index 4a73f22ea..000000000 --- a/coreiface/object.go +++ /dev/null @@ -1,107 +0,0 @@ -package iface - -import ( - "context" - "io" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - - "github.com/ipfs/go-cid" - ipld "github.com/ipfs/go-ipld-format" -) - -// ObjectStat provides information about dag nodes -type ObjectStat struct { - // Cid is the CID of the node - Cid cid.Cid - - // NumLinks is number of links the node contains - NumLinks int - - // BlockSize is size of the raw serialized node - BlockSize int - - // LinksSize is size of the links block section - LinksSize int - - // DataSize is the size of data block section - DataSize int - - // CumulativeSize is size of the tree (BlockSize + link sizes) - CumulativeSize int -} - -// ChangeType denotes type of change in ObjectChange -type ChangeType int - -const ( - // DiffAdd is set when a link was added to the graph - DiffAdd ChangeType = iota - - // DiffRemove is set when a link was removed from the graph - DiffRemove - - // DiffMod is set when a link was changed in the graph - DiffMod -) - -// ObjectChange represents a change ia a graph -type ObjectChange struct { - // Type of the change, either: - // * DiffAdd - Added a link - // * DiffRemove - Removed a link - // * DiffMod - Modified a link - Type ChangeType - - // Path to the changed link - Path string - - // Before holds the link path before the change. Note that when a link is - // added, this will be nil. - Before path.ImmutablePath - - // After holds the link path after the change. Note that when a link is - // removed, this will be nil. - After path.ImmutablePath -} - -// ObjectAPI specifies the interface to MerkleDAG and contains useful utilities -// for manipulating MerkleDAG data structures. -type ObjectAPI interface { - // New creates new, empty (by default) dag-node. - New(context.Context, ...options.ObjectNewOption) (ipld.Node, error) - - // Put imports the data into merkledag - Put(context.Context, io.Reader, ...options.ObjectPutOption) (path.ImmutablePath, error) - - // Get returns the node for the path - Get(context.Context, path.Path) (ipld.Node, error) - - // Data returns reader for data of the node - Data(context.Context, path.Path) (io.Reader, error) - - // Links returns lint or links the node contains - Links(context.Context, path.Path) ([]*ipld.Link, error) - - // Stat returns information about the node - Stat(context.Context, path.Path) (*ObjectStat, error) - - // AddLink adds a link under the specified path. child path can point to a - // subdirectory within the patent which must be present (can be overridden - // with WithCreate option). - AddLink(ctx context.Context, base path.Path, name string, child path.Path, opts ...options.ObjectAddLinkOption) (path.ImmutablePath, error) - - // RmLink removes a link from the node - RmLink(ctx context.Context, base path.Path, link string) (path.ImmutablePath, error) - - // AppendData appends data to the node - AppendData(context.Context, path.Path, io.Reader) (path.ImmutablePath, error) - - // SetData sets the data contained in the node - SetData(context.Context, path.Path, io.Reader) (path.ImmutablePath, error) - - // Diff returns a set of changes needed to transform the first object into the - // second. - Diff(context.Context, path.Path, path.Path) ([]ObjectChange, error) -} diff --git a/coreiface/options/block.go b/coreiface/options/block.go deleted file mode 100644 index 83a43702c..000000000 --- a/coreiface/options/block.go +++ /dev/null @@ -1,165 +0,0 @@ -package options - -import ( - "fmt" - - cid "github.com/ipfs/go-cid" - mc "github.com/multiformats/go-multicodec" - mh "github.com/multiformats/go-multihash" -) - -type BlockPutSettings struct { - CidPrefix cid.Prefix - Pin bool -} - -type BlockRmSettings struct { - Force bool -} - -type ( - BlockPutOption func(*BlockPutSettings) error - BlockRmOption func(*BlockRmSettings) error -) - -func BlockPutOptions(opts ...BlockPutOption) (*BlockPutSettings, error) { - var cidPrefix cid.Prefix - - // Baseline is CIDv1 raw sha2-255-32 (can be tweaked later via opts) - cidPrefix.Version = 1 - cidPrefix.Codec = uint64(mc.Raw) - cidPrefix.MhType = mh.SHA2_256 - cidPrefix.MhLength = -1 // -1 means len is to be calculated during mh.Sum() - - options := &BlockPutSettings{ - CidPrefix: cidPrefix, - Pin: false, - } - - // Apply any overrides - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -func BlockRmOptions(opts ...BlockRmOption) (*BlockRmSettings, error) { - options := &BlockRmSettings{ - Force: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type blockOpts struct{} - -var Block blockOpts - -// CidCodec is the modern option for Block.Put which specifies the multicodec to use -// in the CID returned by the Block.Put operation. -// It uses correct codes from go-multicodec and replaces the old Format now with CIDv1 as the default. -func (blockOpts) CidCodec(codecName string) BlockPutOption { - return func(settings *BlockPutSettings) error { - if codecName == "" { - return nil - } - code, err := codeFromName(codecName) - if err != nil { - return err - } - settings.CidPrefix.Codec = uint64(code) - return nil - } -} - -// Map string to code from go-multicodec -func codeFromName(codecName string) (mc.Code, error) { - var cidCodec mc.Code - err := cidCodec.Set(codecName) - return cidCodec, err -} - -// Format is a legacy option for Block.Put which specifies the multicodec to -// use to serialize the object. -// Provided for backward-compatibility only. Use CidCodec instead. -func (blockOpts) Format(format string) BlockPutOption { - return func(settings *BlockPutSettings) error { - if format == "" { - return nil - } - // Opt-in CIDv0 support for backward-compatibility - if format == "v0" { - settings.CidPrefix.Version = 0 - } - - // Fixup a legacy (invalid) names for dag-pb (0x70) - if format == "v0" || format == "protobuf" { - format = "dag-pb" - } - - // Fixup invalid name for dag-cbor (0x71) - if format == "cbor" { - format = "dag-cbor" - } - - // Set code based on name passed as "format" - code, err := codeFromName(format) - if err != nil { - return err - } - settings.CidPrefix.Codec = uint64(code) - - // If CIDv0, ensure all parameters are compatible - // (in theory go-cid would validate this anyway, but we want to provide better errors) - pref := settings.CidPrefix - if pref.Version == 0 { - if pref.Codec != uint64(mc.DagPb) { - return fmt.Errorf("only dag-pb is allowed with CIDv0") - } - if pref.MhType != mh.SHA2_256 || (pref.MhLength != -1 && pref.MhLength != 32) { - return fmt.Errorf("only sha2-255-32 is allowed with CIDv0") - } - } - - return nil - } -} - -// Hash is an option for Block.Put which specifies the multihash settings to use -// when hashing the object. Default is mh.SHA2_256 (0x12). -// If mhLen is set to -1, default length for the hash will be used -func (blockOpts) Hash(mhType uint64, mhLen int) BlockPutOption { - return func(settings *BlockPutSettings) error { - settings.CidPrefix.MhType = mhType - settings.CidPrefix.MhLength = mhLen - return nil - } -} - -// Pin is an option for Block.Put which specifies whether to (recursively) pin -// added blocks -func (blockOpts) Pin(pin bool) BlockPutOption { - return func(settings *BlockPutSettings) error { - settings.Pin = pin - return nil - } -} - -// Force is an option for Block.Rm which, when set to true, will ignore -// non-existing blocks -func (blockOpts) Force(force bool) BlockRmOption { - return func(settings *BlockRmSettings) error { - settings.Force = force - return nil - } -} diff --git a/coreiface/options/dht.go b/coreiface/options/dht.go deleted file mode 100644 index b43bf3e7a..000000000 --- a/coreiface/options/dht.go +++ /dev/null @@ -1,64 +0,0 @@ -package options - -type DhtProvideSettings struct { - Recursive bool -} - -type DhtFindProvidersSettings struct { - NumProviders int -} - -type ( - DhtProvideOption func(*DhtProvideSettings) error - DhtFindProvidersOption func(*DhtFindProvidersSettings) error -) - -func DhtProvideOptions(opts ...DhtProvideOption) (*DhtProvideSettings, error) { - options := &DhtProvideSettings{ - Recursive: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func DhtFindProvidersOptions(opts ...DhtFindProvidersOption) (*DhtFindProvidersSettings, error) { - options := &DhtFindProvidersSettings{ - NumProviders: 20, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type dhtOpts struct{} - -var Dht dhtOpts - -// Recursive is an option for Dht.Provide which specifies whether to provide -// the given path recursively -func (dhtOpts) Recursive(recursive bool) DhtProvideOption { - return func(settings *DhtProvideSettings) error { - settings.Recursive = recursive - return nil - } -} - -// NumProviders is an option for Dht.FindProviders which specifies the -// number of peers to look for. Default is 20 -func (dhtOpts) NumProviders(numProviders int) DhtFindProvidersOption { - return func(settings *DhtFindProvidersSettings) error { - settings.NumProviders = numProviders - return nil - } -} diff --git a/coreiface/options/global.go b/coreiface/options/global.go deleted file mode 100644 index 90e2586f1..000000000 --- a/coreiface/options/global.go +++ /dev/null @@ -1,47 +0,0 @@ -package options - -type ApiSettings struct { - Offline bool - FetchBlocks bool -} - -type ApiOption func(*ApiSettings) error - -func ApiOptions(opts ...ApiOption) (*ApiSettings, error) { - options := &ApiSettings{ - Offline: false, - FetchBlocks: true, - } - - return ApiOptionsTo(options, opts...) -} - -func ApiOptionsTo(options *ApiSettings, opts ...ApiOption) (*ApiSettings, error) { - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type apiOpts struct{} - -var Api apiOpts - -func (apiOpts) Offline(offline bool) ApiOption { - return func(settings *ApiSettings) error { - settings.Offline = offline - return nil - } -} - -// FetchBlocks when set to false prevents api from fetching blocks from the -// network while allowing other services such as IPNS to still be online -func (apiOpts) FetchBlocks(fetch bool) ApiOption { - return func(settings *ApiSettings) error { - settings.FetchBlocks = fetch - return nil - } -} diff --git a/coreiface/options/key.go b/coreiface/options/key.go deleted file mode 100644 index ebff6d5a7..000000000 --- a/coreiface/options/key.go +++ /dev/null @@ -1,89 +0,0 @@ -package options - -const ( - RSAKey = "rsa" - Ed25519Key = "ed25519" - - DefaultRSALen = 2048 -) - -type KeyGenerateSettings struct { - Algorithm string - Size int -} - -type KeyRenameSettings struct { - Force bool -} - -type ( - KeyGenerateOption func(*KeyGenerateSettings) error - KeyRenameOption func(*KeyRenameSettings) error -) - -func KeyGenerateOptions(opts ...KeyGenerateOption) (*KeyGenerateSettings, error) { - options := &KeyGenerateSettings{ - Algorithm: RSAKey, - Size: -1, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func KeyRenameOptions(opts ...KeyRenameOption) (*KeyRenameSettings, error) { - options := &KeyRenameSettings{ - Force: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type keyOpts struct{} - -var Key keyOpts - -// Type is an option for Key.Generate which specifies which algorithm -// should be used for the key. Default is options.RSAKey -// -// Supported key types: -// * options.RSAKey -// * options.Ed25519Key -func (keyOpts) Type(algorithm string) KeyGenerateOption { - return func(settings *KeyGenerateSettings) error { - settings.Algorithm = algorithm - return nil - } -} - -// Size is an option for Key.Generate which specifies the size of the key to -// generated. Default is -1 -// -// value of -1 means 'use default size for key type': -// - 2048 for RSA -func (keyOpts) Size(size int) KeyGenerateOption { - return func(settings *KeyGenerateSettings) error { - settings.Size = size - return nil - } -} - -// Force is an option for Key.Rename which specifies whether to allow to -// replace existing keys. -func (keyOpts) Force(force bool) KeyRenameOption { - return func(settings *KeyRenameSettings) error { - settings.Force = force - return nil - } -} diff --git a/coreiface/options/name.go b/coreiface/options/name.go deleted file mode 100644 index 7b4b6a8fd..000000000 --- a/coreiface/options/name.go +++ /dev/null @@ -1,131 +0,0 @@ -package options - -import ( - "time" - - "github.com/ipfs/boxo/namesys" -) - -const ( - DefaultNameValidTime = 24 * time.Hour -) - -type NamePublishSettings struct { - ValidTime time.Duration - Key string - TTL *time.Duration - CompatibleWithV1 bool - AllowOffline bool -} - -type NameResolveSettings struct { - Cache bool - - ResolveOpts []namesys.ResolveOption -} - -type ( - NamePublishOption func(*NamePublishSettings) error - NameResolveOption func(*NameResolveSettings) error -) - -func NamePublishOptions(opts ...NamePublishOption) (*NamePublishSettings, error) { - options := &NamePublishSettings{ - ValidTime: DefaultNameValidTime, - Key: "self", - - AllowOffline: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -func NameResolveOptions(opts ...NameResolveOption) (*NameResolveSettings, error) { - options := &NameResolveSettings{ - Cache: true, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -type nameOpts struct{} - -var Name nameOpts - -// ValidTime is an option for Name.Publish which specifies for how long the -// entry will remain valid. Default value is 24h -func (nameOpts) ValidTime(validTime time.Duration) NamePublishOption { - return func(settings *NamePublishSettings) error { - settings.ValidTime = validTime - return nil - } -} - -// Key is an option for Name.Publish which specifies the key to use for -// publishing. Default value is "self" which is the node's own PeerID. -// The key parameter must be either PeerID or keystore key alias. -// -// You can use KeyAPI to list and generate more names and their respective keys. -func (nameOpts) Key(key string) NamePublishOption { - return func(settings *NamePublishSettings) error { - settings.Key = key - return nil - } -} - -// AllowOffline is an option for Name.Publish which specifies whether to allow -// publishing when the node is offline. Default value is false -func (nameOpts) AllowOffline(allow bool) NamePublishOption { - return func(settings *NamePublishSettings) error { - settings.AllowOffline = allow - return nil - } -} - -// TTL is an option for Name.Publish which specifies the time duration the -// published record should be cached for (caution: experimental). -func (nameOpts) TTL(ttl time.Duration) NamePublishOption { - return func(settings *NamePublishSettings) error { - settings.TTL = &ttl - return nil - } -} - -// CompatibleWithV1 is an option for [Name.Publish] which specifies if the -// created record should be backwards compatible with V1 IPNS Records. -func (nameOpts) CompatibleWithV1(compatible bool) NamePublishOption { - return func(settings *NamePublishSettings) error { - settings.CompatibleWithV1 = compatible - return nil - } -} - -// Cache is an option for Name.Resolve which specifies if cache should be used. -// Default value is true -func (nameOpts) Cache(cache bool) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.Cache = cache - return nil - } -} - -func (nameOpts) ResolveOption(opt namesys.ResolveOption) NameResolveOption { - return func(settings *NameResolveSettings) error { - settings.ResolveOpts = append(settings.ResolveOpts, opt) - return nil - } -} diff --git a/coreiface/options/object.go b/coreiface/options/object.go deleted file mode 100644 index b5625a1d6..000000000 --- a/coreiface/options/object.go +++ /dev/null @@ -1,126 +0,0 @@ -package options - -type ObjectNewSettings struct { - Type string -} - -type ObjectPutSettings struct { - InputEnc string - DataType string - Pin bool -} - -type ObjectAddLinkSettings struct { - Create bool -} - -type ( - ObjectNewOption func(*ObjectNewSettings) error - ObjectPutOption func(*ObjectPutSettings) error - ObjectAddLinkOption func(*ObjectAddLinkSettings) error -) - -func ObjectNewOptions(opts ...ObjectNewOption) (*ObjectNewSettings, error) { - options := &ObjectNewSettings{ - Type: "empty", - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func ObjectPutOptions(opts ...ObjectPutOption) (*ObjectPutSettings, error) { - options := &ObjectPutSettings{ - InputEnc: "json", - DataType: "text", - Pin: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func ObjectAddLinkOptions(opts ...ObjectAddLinkOption) (*ObjectAddLinkSettings, error) { - options := &ObjectAddLinkSettings{ - Create: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type objectOpts struct{} - -var Object objectOpts - -// Type is an option for Object.New which allows to change the type of created -// dag node. -// -// Supported types: -// * 'empty' - Empty node -// * 'unixfs-dir' - Empty UnixFS directory -func (objectOpts) Type(t string) ObjectNewOption { - return func(settings *ObjectNewSettings) error { - settings.Type = t - return nil - } -} - -// InputEnc is an option for Object.Put which specifies the input encoding of the -// data. Default is "json". -// -// Supported encodings: -// * "protobuf" -// * "json" -func (objectOpts) InputEnc(e string) ObjectPutOption { - return func(settings *ObjectPutSettings) error { - settings.InputEnc = e - return nil - } -} - -// DataType is an option for Object.Put which specifies the encoding of data -// field when using Json or XML input encoding. -// -// Supported types: -// * "text" (default) -// * "base64" -func (objectOpts) DataType(t string) ObjectPutOption { - return func(settings *ObjectPutSettings) error { - settings.DataType = t - return nil - } -} - -// Pin is an option for Object.Put which specifies whether to pin the added -// objects, default is false -func (objectOpts) Pin(pin bool) ObjectPutOption { - return func(settings *ObjectPutSettings) error { - settings.Pin = pin - return nil - } -} - -// Create is an option for Object.AddLink which specifies whether create required -// directories for the child -func (objectOpts) Create(create bool) ObjectAddLinkOption { - return func(settings *ObjectAddLinkSettings) error { - settings.Create = create - return nil - } -} diff --git a/coreiface/options/pin.go b/coreiface/options/pin.go deleted file mode 100644 index 75c2b8a26..000000000 --- a/coreiface/options/pin.go +++ /dev/null @@ -1,283 +0,0 @@ -package options - -import "fmt" - -// PinAddSettings represent the settings for PinAPI.Add -type PinAddSettings struct { - Recursive bool -} - -// PinLsSettings represent the settings for PinAPI.Ls -type PinLsSettings struct { - Type string -} - -// PinIsPinnedSettings represent the settings for PinAPI.IsPinned -type PinIsPinnedSettings struct { - WithType string -} - -// PinRmSettings represents the settings for PinAPI.Rm -type PinRmSettings struct { - Recursive bool -} - -// PinUpdateSettings represent the settings for PinAPI.Update -type PinUpdateSettings struct { - Unpin bool -} - -// PinAddOption is the signature of an option for PinAPI.Add -type PinAddOption func(*PinAddSettings) error - -// PinLsOption is the signature of an option for PinAPI.Ls -type PinLsOption func(*PinLsSettings) error - -// PinIsPinnedOption is the signature of an option for PinAPI.IsPinned -type PinIsPinnedOption func(*PinIsPinnedSettings) error - -// PinRmOption is the signature of an option for PinAPI.Rm -type PinRmOption func(*PinRmSettings) error - -// PinUpdateOption is the signature of an option for PinAPI.Update -type PinUpdateOption func(*PinUpdateSettings) error - -// PinAddOptions compile a series of PinAddOption into a ready to use -// PinAddSettings and set the default values. -func PinAddOptions(opts ...PinAddOption) (*PinAddSettings, error) { - options := &PinAddSettings{ - Recursive: true, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -// PinLsOptions compile a series of PinLsOption into a ready to use -// PinLsSettings and set the default values. -func PinLsOptions(opts ...PinLsOption) (*PinLsSettings, error) { - options := &PinLsSettings{ - Type: "all", - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -// PinIsPinnedOptions compile a series of PinIsPinnedOption into a ready to use -// PinIsPinnedSettings and set the default values. -func PinIsPinnedOptions(opts ...PinIsPinnedOption) (*PinIsPinnedSettings, error) { - options := &PinIsPinnedSettings{ - WithType: "all", - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -// PinRmOptions compile a series of PinRmOption into a ready to use -// PinRmSettings and set the default values. -func PinRmOptions(opts ...PinRmOption) (*PinRmSettings, error) { - options := &PinRmSettings{ - Recursive: true, - } - - for _, opt := range opts { - if err := opt(options); err != nil { - return nil, err - } - } - - return options, nil -} - -// PinUpdateOptions compile a series of PinUpdateOption into a ready to use -// PinUpdateSettings and set the default values. -func PinUpdateOptions(opts ...PinUpdateOption) (*PinUpdateSettings, error) { - options := &PinUpdateSettings{ - Unpin: true, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -type pinOpts struct { - Ls pinLsOpts - IsPinned pinIsPinnedOpts -} - -// Pin provide an access to all the options for the Pin API. -var Pin pinOpts - -type pinLsOpts struct{} - -// All is an option for Pin.Ls which will make it return all pins. It is -// the default -func (pinLsOpts) All() PinLsOption { - return Pin.Ls.pinType("all") -} - -// Recursive is an option for Pin.Ls which will make it only return recursive -// pins -func (pinLsOpts) Recursive() PinLsOption { - return Pin.Ls.pinType("recursive") -} - -// Direct is an option for Pin.Ls which will make it only return direct (non -// recursive) pins -func (pinLsOpts) Direct() PinLsOption { - return Pin.Ls.pinType("direct") -} - -// Indirect is an option for Pin.Ls which will make it only return indirect pins -// (objects referenced by other recursively pinned objects) -func (pinLsOpts) Indirect() PinLsOption { - return Pin.Ls.pinType("indirect") -} - -// Type is an option for Pin.Ls which will make it only return pins of the given -// type. -// -// Supported values: -// - "direct" - directly pinned objects -// - "recursive" - roots of recursive pins -// - "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// - "all" - all pinned objects (default) -func (pinLsOpts) Type(typeStr string) (PinLsOption, error) { - switch typeStr { - case "all", "direct", "indirect", "recursive": - return Pin.Ls.pinType(typeStr), nil - default: - return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) - } -} - -// pinType is an option for Pin.Ls which allows to specify which pin types should -// be returned -// -// Supported values: -// - "direct" - directly pinned objects -// - "recursive" - roots of recursive pins -// - "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// - "all" - all pinned objects (default) -func (pinLsOpts) pinType(t string) PinLsOption { - return func(settings *PinLsSettings) error { - settings.Type = t - return nil - } -} - -type pinIsPinnedOpts struct{} - -// All is an option for Pin.IsPinned which will make it search in all type of pins. -// It is the default -func (pinIsPinnedOpts) All() PinIsPinnedOption { - return Pin.IsPinned.pinType("all") -} - -// Recursive is an option for Pin.IsPinned which will make it only search in -// recursive pins -func (pinIsPinnedOpts) Recursive() PinIsPinnedOption { - return Pin.IsPinned.pinType("recursive") -} - -// Direct is an option for Pin.IsPinned which will make it only search in direct -// (non recursive) pins -func (pinIsPinnedOpts) Direct() PinIsPinnedOption { - return Pin.IsPinned.pinType("direct") -} - -// Indirect is an option for Pin.IsPinned which will make it only search indirect -// pins (objects referenced by other recursively pinned objects) -func (pinIsPinnedOpts) Indirect() PinIsPinnedOption { - return Pin.IsPinned.pinType("indirect") -} - -// Type is an option for Pin.IsPinned which will make it only search pins of the given -// type. -// -// Supported values: -// - "direct" - directly pinned objects -// - "recursive" - roots of recursive pins -// - "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// - "all" - all pinned objects (default) -func (pinIsPinnedOpts) Type(typeStr string) (PinIsPinnedOption, error) { - switch typeStr { - case "all", "direct", "indirect", "recursive": - return Pin.IsPinned.pinType(typeStr), nil - default: - return nil, fmt.Errorf("invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr) - } -} - -// pinType is an option for Pin.IsPinned which allows to specify which pin type the given -// pin is expected to be, speeding up the research. -// -// Supported values: -// - "direct" - directly pinned objects -// - "recursive" - roots of recursive pins -// - "indirect" - indirectly pinned objects (referenced by recursively pinned -// objects) -// - "all" - all pinned objects (default) -func (pinIsPinnedOpts) pinType(t string) PinIsPinnedOption { - return func(settings *PinIsPinnedSettings) error { - settings.WithType = t - return nil - } -} - -// Recursive is an option for Pin.Add which specifies whether to pin an entire -// object tree or just one object. Default: true -func (pinOpts) Recursive(recursive bool) PinAddOption { - return func(settings *PinAddSettings) error { - settings.Recursive = recursive - return nil - } -} - -// RmRecursive is an option for Pin.Rm which specifies whether to recursively -// unpin the object linked to by the specified object(s). This does not remove -// indirect pins referenced by other recursive pins. -func (pinOpts) RmRecursive(recursive bool) PinRmOption { - return func(settings *PinRmSettings) error { - settings.Recursive = recursive - return nil - } -} - -// Unpin is an option for Pin.Update which specifies whether to remove the old pin. -// Default is true. -func (pinOpts) Unpin(unpin bool) PinUpdateOption { - return func(settings *PinUpdateSettings) error { - settings.Unpin = unpin - return nil - } -} diff --git a/coreiface/options/pubsub.go b/coreiface/options/pubsub.go deleted file mode 100644 index 839ef97b1..000000000 --- a/coreiface/options/pubsub.go +++ /dev/null @@ -1,60 +0,0 @@ -package options - -type PubSubPeersSettings struct { - Topic string -} - -type PubSubSubscribeSettings struct { - Discover bool -} - -type ( - PubSubPeersOption func(*PubSubPeersSettings) error - PubSubSubscribeOption func(*PubSubSubscribeSettings) error -) - -func PubSubPeersOptions(opts ...PubSubPeersOption) (*PubSubPeersSettings, error) { - options := &PubSubPeersSettings{ - Topic: "", - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -func PubSubSubscribeOptions(opts ...PubSubSubscribeOption) (*PubSubSubscribeSettings, error) { - options := &PubSubSubscribeSettings{ - Discover: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - return options, nil -} - -type pubsubOpts struct{} - -var PubSub pubsubOpts - -func (pubsubOpts) Topic(topic string) PubSubPeersOption { - return func(settings *PubSubPeersSettings) error { - settings.Topic = topic - return nil - } -} - -func (pubsubOpts) Discover(discover bool) PubSubSubscribeOption { - return func(settings *PubSubSubscribeSettings) error { - settings.Discover = discover - return nil - } -} diff --git a/coreiface/options/routing.go b/coreiface/options/routing.go deleted file mode 100644 index d66d44a0d..000000000 --- a/coreiface/options/routing.go +++ /dev/null @@ -1,35 +0,0 @@ -package options - -type RoutingPutSettings struct { - AllowOffline bool -} - -type RoutingPutOption func(*RoutingPutSettings) error - -func RoutingPutOptions(opts ...RoutingPutOption) (*RoutingPutSettings, error) { - options := &RoutingPutSettings{ - AllowOffline: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -type putOpts struct{} - -var Put putOpts - -// AllowOffline is an option for Routing.Put which specifies whether to allow -// publishing when the node is offline. Default value is false -func (putOpts) AllowOffline(allow bool) RoutingPutOption { - return func(settings *RoutingPutSettings) error { - settings.AllowOffline = allow - return nil - } -} diff --git a/coreiface/options/unixfs.go b/coreiface/options/unixfs.go deleted file mode 100644 index f00fffb87..000000000 --- a/coreiface/options/unixfs.go +++ /dev/null @@ -1,295 +0,0 @@ -package options - -import ( - "errors" - "fmt" - - dag "github.com/ipfs/boxo/ipld/merkledag" - cid "github.com/ipfs/go-cid" - mh "github.com/multiformats/go-multihash" -) - -type Layout int - -const ( - BalancedLayout Layout = iota - TrickleLayout -) - -type UnixfsAddSettings struct { - CidVersion int - MhType uint64 - - Inline bool - InlineLimit int - RawLeaves bool - RawLeavesSet bool - - Chunker string - Layout Layout - - Pin bool - OnlyHash bool - FsCache bool - NoCopy bool - - Events chan<- interface{} - Silent bool - Progress bool -} - -type UnixfsLsSettings struct { - ResolveChildren bool - UseCumulativeSize bool -} - -type ( - UnixfsAddOption func(*UnixfsAddSettings) error - UnixfsLsOption func(*UnixfsLsSettings) error -) - -func UnixfsAddOptions(opts ...UnixfsAddOption) (*UnixfsAddSettings, cid.Prefix, error) { - options := &UnixfsAddSettings{ - CidVersion: -1, - MhType: mh.SHA2_256, - - Inline: false, - InlineLimit: 32, - RawLeaves: false, - RawLeavesSet: false, - - Chunker: "size-262144", - Layout: BalancedLayout, - - Pin: false, - OnlyHash: false, - FsCache: false, - NoCopy: false, - - Events: nil, - Silent: false, - Progress: false, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, cid.Prefix{}, err - } - } - - // nocopy -> rawblocks - if options.NoCopy && !options.RawLeaves { - // fixed? - if options.RawLeavesSet { - return nil, cid.Prefix{}, fmt.Errorf("nocopy option requires '--raw-leaves' to be enabled as well") - } - - // No, satisfy mandatory constraint. - options.RawLeaves = true - } - - // (hash != "sha2-256") -> CIDv1 - if options.MhType != mh.SHA2_256 { - switch options.CidVersion { - case 0: - return nil, cid.Prefix{}, errors.New("CIDv0 only supports sha2-256") - case 1, -1: - options.CidVersion = 1 - default: - return nil, cid.Prefix{}, fmt.Errorf("unknown CID version: %d", options.CidVersion) - } - } else { - if options.CidVersion < 0 { - // Default to CIDv0 - options.CidVersion = 0 - } - } - - // cidV1 -> raw blocks (by default) - if options.CidVersion > 0 && !options.RawLeavesSet { - options.RawLeaves = true - } - - prefix, err := dag.PrefixForCidVersion(options.CidVersion) - if err != nil { - return nil, cid.Prefix{}, err - } - - prefix.MhType = options.MhType - prefix.MhLength = -1 - - return options, prefix, nil -} - -func UnixfsLsOptions(opts ...UnixfsLsOption) (*UnixfsLsSettings, error) { - options := &UnixfsLsSettings{ - ResolveChildren: true, - } - - for _, opt := range opts { - err := opt(options) - if err != nil { - return nil, err - } - } - - return options, nil -} - -type unixfsOpts struct{} - -var Unixfs unixfsOpts - -// CidVersion specifies which CID version to use. Defaults to 0 unless an option -// that depends on CIDv1 is passed. -func (unixfsOpts) CidVersion(version int) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.CidVersion = version - return nil - } -} - -// Hash function to use. Implies CIDv1 if not set to sha2-256 (default). -// -// Table of functions is declared in https://github.com/multiformats/go-multihash/blob/master/multihash.go -func (unixfsOpts) Hash(mhtype uint64) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.MhType = mhtype - return nil - } -} - -// RawLeaves specifies whether to use raw blocks for leaves (data nodes with no -// links) instead of wrapping them with unixfs structures. -func (unixfsOpts) RawLeaves(enable bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.RawLeaves = enable - settings.RawLeavesSet = true - return nil - } -} - -// Inline tells the adder to inline small blocks into CIDs -func (unixfsOpts) Inline(enable bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Inline = enable - return nil - } -} - -// InlineLimit sets the amount of bytes below which blocks will be encoded -// directly into CID instead of being stored and addressed by it's hash. -// Specifying this option won't enable block inlining. For that use `Inline` -// option. Default: 32 bytes -// -// Note that while there is no hard limit on the number of bytes, it should be -// kept at a reasonably low value, such as 64; implementations may choose to -// reject anything larger. -func (unixfsOpts) InlineLimit(limit int) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.InlineLimit = limit - return nil - } -} - -// Chunker specifies settings for the chunking algorithm to use. -// -// Default: size-262144, formats: -// size-[bytes] - Simple chunker splitting data into blocks of n bytes -// rabin-[min]-[avg]-[max] - Rabin chunker -func (unixfsOpts) Chunker(chunker string) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Chunker = chunker - return nil - } -} - -// Layout tells the adder how to balance data between leaves. -// options.BalancedLayout is the default, it's optimized for static seekable -// files. -// options.TrickleLayout is optimized for streaming data, -func (unixfsOpts) Layout(layout Layout) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Layout = layout - return nil - } -} - -// Pin tells the adder to pin the file root recursively after adding -func (unixfsOpts) Pin(pin bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Pin = pin - return nil - } -} - -// HashOnly will make the adder calculate data hash without storing it in the -// blockstore or announcing it to the network -func (unixfsOpts) HashOnly(hashOnly bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.OnlyHash = hashOnly - return nil - } -} - -// Events specifies channel which will be used to report events about ongoing -// Add operation. -// -// Note that if this channel blocks it may slowdown the adder -func (unixfsOpts) Events(sink chan<- interface{}) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Events = sink - return nil - } -} - -// Silent reduces event output -func (unixfsOpts) Silent(silent bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Silent = silent - return nil - } -} - -// Progress tells the adder whether to enable progress events -func (unixfsOpts) Progress(enable bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.Progress = enable - return nil - } -} - -// FsCache tells the adder to check the filestore for pre-existing blocks -// -// Experimental -func (unixfsOpts) FsCache(enable bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.FsCache = enable - return nil - } -} - -// NoCopy tells the adder to add the files using filestore. Implies RawLeaves. -// -// Experimental -func (unixfsOpts) Nocopy(enable bool) UnixfsAddOption { - return func(settings *UnixfsAddSettings) error { - settings.NoCopy = enable - return nil - } -} - -func (unixfsOpts) ResolveChildren(resolve bool) UnixfsLsOption { - return func(settings *UnixfsLsSettings) error { - settings.ResolveChildren = resolve - return nil - } -} - -func (unixfsOpts) UseCumulativeSize(use bool) UnixfsLsOption { - return func(settings *UnixfsLsSettings) error { - settings.UseCumulativeSize = use - return nil - } -} diff --git a/coreiface/pin.go b/coreiface/pin.go deleted file mode 100644 index 057516d08..000000000 --- a/coreiface/pin.go +++ /dev/null @@ -1,66 +0,0 @@ -package iface - -import ( - "context" - - "github.com/ipfs/boxo/path" - - "github.com/ipfs/boxo/coreiface/options" -) - -// Pin holds information about pinned resource -type Pin interface { - // Path to the pinned object - Path() path.ImmutablePath - - // Type of the pin - Type() string - - // if not nil, an error happened. Everything else should be ignored. - Err() error -} - -// PinStatus holds information about pin health -type PinStatus interface { - // Ok indicates whether the pin has been verified to be correct - Ok() bool - - // BadNodes returns any bad (usually missing) nodes from the pin - BadNodes() []BadPinNode - - // if not nil, an error happened. Everything else should be ignored. - Err() error -} - -// BadPinNode is a node that has been marked as bad by Pin.Verify -type BadPinNode interface { - // Path is the path of the node - Path() path.ImmutablePath - - // Err is the reason why the node has been marked as bad - Err() error -} - -// PinAPI specifies the interface to pining -type PinAPI interface { - // Add creates new pin, be default recursive - pinning the whole referenced - // tree - Add(context.Context, path.Path, ...options.PinAddOption) error - - // Ls returns list of pinned objects on this node - Ls(context.Context, ...options.PinLsOption) (<-chan Pin, error) - - // IsPinned returns whether or not the given cid is pinned - // and an explanation of why its pinned - IsPinned(context.Context, path.Path, ...options.PinIsPinnedOption) (string, bool, error) - - // Rm removes pin for object specified by the path - Rm(context.Context, path.Path, ...options.PinRmOption) error - - // Update changes one pin to another, skipping checks for matching paths in - // the old tree - Update(ctx context.Context, from path.Path, to path.Path, opts ...options.PinUpdateOption) error - - // Verify verifies the integrity of pinned objects - Verify(context.Context) (<-chan PinStatus, error) -} diff --git a/coreiface/pubsub.go b/coreiface/pubsub.go deleted file mode 100644 index bbd1da4ec..000000000 --- a/coreiface/pubsub.go +++ /dev/null @@ -1,48 +0,0 @@ -package iface - -import ( - "context" - "io" - - "github.com/ipfs/boxo/coreiface/options" - - "github.com/libp2p/go-libp2p/core/peer" -) - -// PubSubSubscription is an active PubSub subscription -type PubSubSubscription interface { - io.Closer - - // Next return the next incoming message - Next(context.Context) (PubSubMessage, error) -} - -// PubSubMessage is a single PubSub message -type PubSubMessage interface { - // From returns id of a peer from which the message has arrived - From() peer.ID - - // Data returns the message body - Data() []byte - - // Seq returns message identifier - Seq() []byte - - // Topics returns list of topics this message was set to - Topics() []string -} - -// PubSubAPI specifies the interface to PubSub -type PubSubAPI interface { - // Ls lists subscribed topics by name - Ls(context.Context) ([]string, error) - - // Peers list peers we are currently pubsubbing with - Peers(context.Context, ...options.PubSubPeersOption) ([]peer.ID, error) - - // Publish a message to a given pubsub topic - Publish(context.Context, string, []byte) error - - // Subscribe to messages on a given topic - Subscribe(context.Context, string, ...options.PubSubSubscribeOption) (PubSubSubscription, error) -} diff --git a/coreiface/routing.go b/coreiface/routing.go deleted file mode 100644 index 5099c3de0..000000000 --- a/coreiface/routing.go +++ /dev/null @@ -1,16 +0,0 @@ -package iface - -import ( - "context" - - "github.com/ipfs/boxo/coreiface/options" -) - -// RoutingAPI specifies the interface to the routing layer. -type RoutingAPI interface { - // Get retrieves the best value for a given key - Get(context.Context, string) ([]byte, error) - - // Put sets a value for a given key - Put(ctx context.Context, key string, value []byte, opts ...options.RoutingPutOption) error -} diff --git a/coreiface/swarm.go b/coreiface/swarm.go deleted file mode 100644 index 9aa5466ba..000000000 --- a/coreiface/swarm.go +++ /dev/null @@ -1,57 +0,0 @@ -package iface - -import ( - "context" - "errors" - "time" - - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/protocol" - - ma "github.com/multiformats/go-multiaddr" -) - -var ( - ErrNotConnected = errors.New("not connected") - ErrConnNotFound = errors.New("conn not found") -) - -// ConnectionInfo contains information about a peer -type ConnectionInfo interface { - // ID returns PeerID - ID() peer.ID - - // Address returns the multiaddress via which we are connected with the peer - Address() ma.Multiaddr - - // Direction returns which way the connection was established - Direction() network.Direction - - // Latency returns last known round trip time to the peer - Latency() (time.Duration, error) - - // Streams returns list of streams established with the peer - Streams() ([]protocol.ID, error) -} - -// SwarmAPI specifies the interface to libp2p swarm -type SwarmAPI interface { - // Connect to a given peer - Connect(context.Context, peer.AddrInfo) error - - // Disconnect from a given address - Disconnect(context.Context, ma.Multiaddr) error - - // Peers returns the list of peers we are connected to - Peers(context.Context) ([]ConnectionInfo, error) - - // KnownAddrs returns the list of all addresses this node is aware of - KnownAddrs(context.Context) (map[peer.ID][]ma.Multiaddr, error) - - // LocalAddrs returns the list of announced listening addresses - LocalAddrs(context.Context) ([]ma.Multiaddr, error) - - // ListenAddrs returns the list of all listening addresses - ListenAddrs(context.Context) ([]ma.Multiaddr, error) -} diff --git a/coreiface/tests/api.go b/coreiface/tests/api.go deleted file mode 100644 index a66e2abeb..000000000 --- a/coreiface/tests/api.go +++ /dev/null @@ -1,110 +0,0 @@ -package tests - -import ( - "context" - "errors" - "testing" - "time" - - coreiface "github.com/ipfs/boxo/coreiface" -) - -var errAPINotImplemented = errors.New("api not implemented") - -type Provider interface { - // Make creates n nodes. fullIdentity set to false can be ignored - MakeAPISwarm(t *testing.T, ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) -} - -func (tp *TestSuite) makeAPISwarm(t *testing.T, ctx context.Context, fullIdentity bool, online bool, n int) ([]coreiface.CoreAPI, error) { - if tp.apis != nil { - tp.apis <- 1 - go func() { - <-ctx.Done() - tp.apis <- -1 - }() - } - - return tp.Provider.MakeAPISwarm(t, ctx, fullIdentity, online, n) -} - -func (tp *TestSuite) makeAPI(t *testing.T, ctx context.Context) (coreiface.CoreAPI, error) { - api, err := tp.makeAPISwarm(t, ctx, false, false, 1) - if err != nil { - return nil, err - } - - return api[0], nil -} - -func (tp *TestSuite) makeAPIWithIdentityAndOffline(t *testing.T, ctx context.Context) (coreiface.CoreAPI, error) { - api, err := tp.makeAPISwarm(t, ctx, true, false, 1) - if err != nil { - return nil, err - } - - return api[0], nil -} - -func (tp *TestSuite) MakeAPISwarm(t *testing.T, ctx context.Context, n int) ([]coreiface.CoreAPI, error) { - return tp.makeAPISwarm(t, ctx, true, true, n) -} - -type TestSuite struct { - Provider - - apis chan int -} - -func TestApi(p Provider) func(t *testing.T) { - running := 1 - apis := make(chan int) - zeroRunning := make(chan struct{}) - go func() { - for i := range apis { - running += i - if running < 1 { - close(zeroRunning) - return - } - } - }() - - tp := &TestSuite{Provider: p, apis: apis} - - return func(t *testing.T) { - t.Run("Block", tp.TestBlock) - t.Run("Dag", tp.TestDag) - t.Run("Dht", tp.TestDht) - t.Run("Key", tp.TestKey) - t.Run("Name", tp.TestName) - t.Run("Object", tp.TestObject) - t.Run("Path", tp.TestPath) - t.Run("Pin", tp.TestPin) - t.Run("PubSub", tp.TestPubSub) - t.Run("Routing", tp.TestRouting) - t.Run("Unixfs", tp.TestUnixfs) - - apis <- -1 - t.Run("TestsCancelCtx", func(t *testing.T) { - select { - case <-zeroRunning: - case <-time.After(time.Second): - t.Errorf("%d test swarms(s) not closed", running) - } - }) - } -} - -func (tp *TestSuite) hasApi(t *testing.T, tf func(coreiface.CoreAPI) error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - if err := tf(api); err != nil { - t.Fatal(api) - } -} diff --git a/coreiface/tests/block.go b/coreiface/tests/block.go deleted file mode 100644 index 6e254063e..000000000 --- a/coreiface/tests/block.go +++ /dev/null @@ -1,353 +0,0 @@ -package tests - -import ( - "bytes" - "context" - "io" - "strings" - "testing" - - coreiface "github.com/ipfs/boxo/coreiface" - opt "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - ipld "github.com/ipfs/go-ipld-format" - mh "github.com/multiformats/go-multihash" -) - -var ( - pbCidV0 = "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN" // dag-pb - pbCid = "bafybeiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // dag-pb - rawCid = "bafkreiffndsajwhk3lwjewwdxqntmjm4b5wxaaanokonsggenkbw6slwk4" // raw bytes - cborCid = "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" // dag-cbor - cborKCid = "bafyr2qgsohbwdlk7ajmmbb4lhoytmest4wdbe5xnexfvtxeatuyqqmwv3fgxp3pmhpc27gwey2cct56gloqefoqwcf3yqiqzsaqb7p4jefhcw" // dag-cbor keccak-512 -) - -// dag-pb -func pbBlock() io.Reader { - return bytes.NewReader([]byte{10, 12, 8, 2, 18, 6, 104, 101, 108, 108, 111, 10, 24, 6}) -} - -// dag-cbor -func cborBlock() io.Reader { - return bytes.NewReader([]byte{101, 72, 101, 108, 108, 111}) -} - -func (tp *TestSuite) TestBlock(t *testing.T) { - tp.hasApi(t, func(api coreiface.CoreAPI) error { - if api.Block() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestBlockPut (get raw CIDv1)", tp.TestBlockPut) - t.Run("TestBlockPutCidCodec: dag-pb", tp.TestBlockPutCidCodecDagPb) - t.Run("TestBlockPutCidCodec: dag-cbor", tp.TestBlockPutCidCodecDagCbor) - t.Run("TestBlockPutFormat (legacy): cbor → dag-cbor", tp.TestBlockPutFormatDagCbor) - t.Run("TestBlockPutFormat (legacy): protobuf → dag-pb", tp.TestBlockPutFormatDagPb) - t.Run("TestBlockPutFormat (legacy): v0 → CIDv0", tp.TestBlockPutFormatV0) - t.Run("TestBlockPutHash", tp.TestBlockPutHash) - t.Run("TestBlockGet", tp.TestBlockGet) - t.Run("TestBlockRm", tp.TestBlockRm) - t.Run("TestBlockStat", tp.TestBlockStat) - t.Run("TestBlockPin", tp.TestBlockPin) -} - -// when no opts are passed, produced CID has 'raw' codec -func (tp *TestSuite) TestBlockPut(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, pbBlock()) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != rawCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -// Format is deprecated, it used invalid codec names. -// Confirm 'cbor' gets fixed to 'dag-cbor' -func (tp *TestSuite) TestBlockPutFormatDagCbor(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, cborBlock(), opt.Block.Format("cbor")) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != cborCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -// Format is deprecated, it used invalid codec names. -// Confirm 'protobuf' got fixed to 'dag-pb' -func (tp *TestSuite) TestBlockPutFormatDagPb(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("protobuf")) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != pbCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -// Format is deprecated, it used invalid codec names. -// Confirm fake codec 'v0' got fixed to CIDv0 (with implicit dag-pb codec) -func (tp *TestSuite) TestBlockPutFormatV0(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, pbBlock(), opt.Block.Format("v0")) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != pbCidV0 { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -func (tp *TestSuite) TestBlockPutCidCodecDagCbor(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, cborBlock(), opt.Block.CidCodec("dag-cbor")) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != cborCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -func (tp *TestSuite) TestBlockPutCidCodecDagPb(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, pbBlock(), opt.Block.CidCodec("dag-pb")) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != pbCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -func (tp *TestSuite) TestBlockPutHash(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put( - ctx, - cborBlock(), - opt.Block.Hash(mh.KECCAK_512, -1), - opt.Block.CidCodec("dag-cbor"), - ) - if err != nil { - t.Fatal(err) - } - - if res.Path().RootCid().String() != cborKCid { - t.Errorf("got wrong cid: %s", res.Path().RootCid().String()) - } -} - -func (tp *TestSuite) TestBlockGet(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Block().Get(ctx, res.Path()) - if err != nil { - t.Fatal(err) - } - - d, err := io.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if string(d) != "Hello" { - t.Error("didn't get correct data back") - } - - p := path.FromCid(res.Path().RootCid()) - - rp, _, err := api.ResolvePath(ctx, p) - if err != nil { - t.Fatal(err) - } - if rp.RootCid().String() != res.Path().RootCid().String() { - t.Error("paths didn't match") - } -} - -func (tp *TestSuite) TestBlockRm(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Block().Get(ctx, res.Path()) - if err != nil { - t.Fatal(err) - } - - d, err := io.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if string(d) != "Hello" { - t.Error("didn't get correct data back") - } - - err = api.Block().Rm(ctx, res.Path()) - if err != nil { - t.Fatal(err) - } - - _, err = api.Block().Get(ctx, res.Path()) - if err == nil { - t.Fatal("expected err to exist") - } - if !ipld.IsNotFound(err) { - t.Errorf("unexpected error; %s", err.Error()) - } - - err = api.Block().Rm(ctx, res.Path()) - if err == nil { - t.Fatal("expected err to exist") - } - if !ipld.IsNotFound(err) { - t.Errorf("unexpected error; %s", err.Error()) - } - - err = api.Block().Rm(ctx, res.Path(), opt.Block.Force(true)) - if err != nil { - t.Fatal(err) - } -} - -func (tp *TestSuite) TestBlockStat(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - res, err := api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) - if err != nil { - t.Fatal(err) - } - - stat, err := api.Block().Stat(ctx, res.Path()) - if err != nil { - t.Fatal(err) - } - - if stat.Path().String() != res.Path().String() { - t.Error("paths don't match") - } - - if stat.Size() != len("Hello") { - t.Error("length doesn't match") - } -} - -func (tp *TestSuite) TestBlockPin(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Block().Put(ctx, strings.NewReader(`Hello`), opt.Block.Format("raw")) - if err != nil { - t.Fatal(err) - } - - if pins, err := api.Pin().Ls(ctx); err != nil || len(pins) != 0 { - t.Fatal("expected 0 pins") - } - - res, err := api.Block().Put( - ctx, - strings.NewReader(`Hello`), - opt.Block.Pin(true), - opt.Block.Format("raw"), - ) - if err != nil { - t.Fatal(err) - } - - pins, err := accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - if len(pins) != 1 { - t.Fatal("expected 1 pin") - } - if pins[0].Type() != "recursive" { - t.Error("expected a recursive pin") - } - if pins[0].Path().String() != res.Path().String() { - t.Error("pin path didn't match") - } -} diff --git a/coreiface/tests/dag.go b/coreiface/tests/dag.go deleted file mode 100644 index a106788d6..000000000 --- a/coreiface/tests/dag.go +++ /dev/null @@ -1,199 +0,0 @@ -package tests - -import ( - "context" - "math" - "strings" - "testing" - - coreiface "github.com/ipfs/boxo/coreiface" - "github.com/ipfs/boxo/path" - - ipldcbor "github.com/ipfs/go-ipld-cbor" - ipld "github.com/ipfs/go-ipld-format" - mh "github.com/multiformats/go-multihash" -) - -func (tp *TestSuite) TestDag(t *testing.T) { - tp.hasApi(t, func(api coreiface.CoreAPI) error { - if api.Dag() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestPut", tp.TestPut) - t.Run("TestPutWithHash", tp.TestPutWithHash) - t.Run("TestPath", tp.TestDagPath) - t.Run("TestTree", tp.TestTree) - t.Run("TestBatch", tp.TestBatch) -} - -var treeExpected = map[string]struct{}{ - "a": {}, - "b": {}, - "c": {}, - "c/d": {}, - "c/e": {}, -} - -func (tp *TestSuite) TestPut(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { - t.Errorf("got wrong cid: %s", nd.Cid().String()) - } -} - -func (tp *TestSuite) TestPutWithHash(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), mh.SHA3_256, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - if nd.Cid().String() != "bafyrmifu7haikttpqqgc5ewvmp76z3z4ebp7h2ph4memw7dq4nt6btmxny" { - t.Errorf("got wrong cid: %s", nd.Cid().String()) - } -} - -func (tp *TestSuite) TestDagPath(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - snd, err := ipldcbor.FromJSON(strings.NewReader(`"foo"`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, snd) - if err != nil { - t.Fatal(err) - } - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+snd.Cid().String()+`"}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - p, err := path.Join(path.FromCid(nd.Cid()), "lnk") - if err != nil { - t.Fatal(err) - } - - rp, _, err := api.ResolvePath(ctx, p) - if err != nil { - t.Fatal(err) - } - - ndd, err := api.Dag().Get(ctx, rp.RootCid()) - if err != nil { - t.Fatal(err) - } - - if ndd.Cid().String() != snd.Cid().String() { - t.Errorf("got unexpected cid %s, expected %s", ndd.Cid().String(), snd.Cid().String()) - } -} - -func (tp *TestSuite) TestTree(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"a": 123, "b": "foo", "c": {"d": 321, "e": 111}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - res, err := api.Dag().Get(ctx, nd.Cid()) - if err != nil { - t.Fatal(err) - } - - lst := res.Tree("", -1) - if len(lst) != len(treeExpected) { - t.Errorf("tree length of %d doesn't match expected %d", len(lst), len(treeExpected)) - } - - for _, ent := range lst { - if _, ok := treeExpected[ent]; !ok { - t.Errorf("unexpected tree entry %s", ent) - } - } -} - -func (tp *TestSuite) TestBatch(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd, err := ipldcbor.FromJSON(strings.NewReader(`"Hello"`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - if nd.Cid().String() != "bafyreicnga62zhxnmnlt6ymq5hcbsg7gdhqdu6z4ehu3wpjhvqnflfy6nm" { - t.Errorf("got wrong cid: %s", nd.Cid().String()) - } - - _, err = api.Dag().Get(ctx, nd.Cid()) - if err == nil || !strings.Contains(err.Error(), "not found") { - t.Fatal(err) - } - - if err := api.Dag().AddMany(ctx, []ipld.Node{nd}); err != nil { - t.Fatal(err) - } - - _, err = api.Dag().Get(ctx, nd.Cid()) - if err != nil { - t.Fatal(err) - } -} diff --git a/coreiface/tests/dht.go b/coreiface/tests/dht.go deleted file mode 100644 index 3b3ac1d61..000000000 --- a/coreiface/tests/dht.go +++ /dev/null @@ -1,166 +0,0 @@ -package tests - -import ( - "context" - "io" - "testing" - "time" - - iface "github.com/ipfs/boxo/coreiface" - "github.com/ipfs/boxo/coreiface/options" -) - -func (tp *TestSuite) TestDht(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.Dht() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestDhtFindPeer", tp.TestDhtFindPeer) - t.Run("TestDhtFindProviders", tp.TestDhtFindProviders) - t.Run("TestDhtProvide", tp.TestDhtProvide) -} - -func (tp *TestSuite) TestDhtFindPeer(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 5) - if err != nil { - t.Fatal(err) - } - - self0, err := apis[0].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - laddrs0, err := apis[0].Swarm().LocalAddrs(ctx) - if err != nil { - t.Fatal(err) - } - if len(laddrs0) != 1 { - t.Fatal("unexpected number of local addrs") - } - - time.Sleep(3 * time.Second) - - pi, err := apis[2].Dht().FindPeer(ctx, self0.ID()) - if err != nil { - t.Fatal(err) - } - - if pi.Addrs[0].String() != laddrs0[0].String() { - t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) - } - - self2, err := apis[2].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - pi, err = apis[1].Dht().FindPeer(ctx, self2.ID()) - if err != nil { - t.Fatal(err) - } - - laddrs2, err := apis[2].Swarm().LocalAddrs(ctx) - if err != nil { - t.Fatal(err) - } - if len(laddrs2) != 1 { - t.Fatal("unexpected number of local addrs") - } - - if pi.Addrs[0].String() != laddrs2[0].String() { - t.Errorf("got unexpected address from FindPeer: %s", pi.Addrs[0].String()) - } -} - -func (tp *TestSuite) TestDhtFindProviders(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 5) - if err != nil { - t.Fatal(err) - } - - p, err := addTestObject(ctx, apis[0]) - if err != nil { - t.Fatal(err) - } - - time.Sleep(3 * time.Second) - - out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) - if err != nil { - t.Fatal(err) - } - - provider := <-out - - self0, err := apis[0].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - if provider.ID.String() != self0.ID().String() { - t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) - } -} - -func (tp *TestSuite) TestDhtProvide(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 5) - if err != nil { - t.Fatal(err) - } - - off0, err := apis[0].WithOptions(options.Api.Offline(true)) - if err != nil { - t.Fatal(err) - } - - s, err := off0.Block().Put(ctx, &io.LimitedReader{R: rnd, N: 4092}) - if err != nil { - t.Fatal(err) - } - - p := s.Path() - - time.Sleep(3 * time.Second) - - out, err := apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) - if err != nil { - t.Fatal(err) - } - - _, ok := <-out - - if ok { - t.Fatal("did not expect to find any providers") - } - - self0, err := apis[0].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - err = apis[0].Dht().Provide(ctx, p) - if err != nil { - t.Fatal(err) - } - - out, err = apis[2].Dht().FindProviders(ctx, p, options.Dht.NumProviders(1)) - if err != nil { - t.Fatal(err) - } - - provider := <-out - - if provider.ID.String() != self0.ID().String() { - t.Errorf("got wrong provider: %s != %s", provider.ID.String(), self0.ID().String()) - } -} diff --git a/coreiface/tests/key.go b/coreiface/tests/key.go deleted file mode 100644 index 0b755380e..000000000 --- a/coreiface/tests/key.go +++ /dev/null @@ -1,538 +0,0 @@ -package tests - -import ( - "context" - "strings" - "testing" - - iface "github.com/ipfs/boxo/coreiface" - opt "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/go-cid" - mbase "github.com/multiformats/go-multibase" -) - -func (tp *TestSuite) TestKey(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.Key() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestListSelf", tp.TestListSelf) - t.Run("TestRenameSelf", tp.TestRenameSelf) - t.Run("TestRemoveSelf", tp.TestRemoveSelf) - t.Run("TestGenerate", tp.TestGenerate) - t.Run("TestGenerateSize", tp.TestGenerateSize) - t.Run("TestGenerateType", tp.TestGenerateType) - t.Run("TestGenerateExisting", tp.TestGenerateExisting) - t.Run("TestList", tp.TestList) - t.Run("TestRename", tp.TestRename) - t.Run("TestRenameToSelf", tp.TestRenameToSelf) - t.Run("TestRenameToSelfForce", tp.TestRenameToSelfForce) - t.Run("TestRenameOverwriteNoForce", tp.TestRenameOverwriteNoForce) - t.Run("TestRenameOverwrite", tp.TestRenameOverwrite) - t.Run("TestRenameSameNameNoForce", tp.TestRenameSameNameNoForce) - t.Run("TestRenameSameName", tp.TestRenameSameName) - t.Run("TestRemove", tp.TestRemove) -} - -func (tp *TestSuite) TestListSelf(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - return - } - - self, err := api.Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - keys, err := api.Key().List(ctx) - if err != nil { - t.Fatalf("failed to list keys: %s", err) - return - } - - if len(keys) != 1 { - t.Fatalf("there should be 1 key (self), got %d", len(keys)) - return - } - - if keys[0].Name() != "self" { - t.Errorf("expected the key to be called 'self', got '%s'", keys[0].Name()) - } - - if keys[0].Path().String() != "/ipns/"+iface.FormatKeyID(self.ID()) { - t.Errorf("expected the key to have path '/ipns/%s', got '%s'", iface.FormatKeyID(self.ID()), keys[0].Path().String()) - } -} - -func (tp *TestSuite) TestRenameSelf(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - return - } - - _, _, err = api.Key().Rename(ctx, "self", "foo") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot rename key with name 'self'") { - t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) - } - } - - _, _, err = api.Key().Rename(ctx, "self", "foo", opt.Key.Force(true)) - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot rename key with name 'self'") { - t.Fatalf("expected error 'cannot rename key with name 'self'', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestRemoveSelf(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - return - } - - _, err = api.Key().Remove(ctx, "self") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot remove key with name 'self'") { - t.Fatalf("expected error 'cannot remove key with name 'self'', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestGenerate(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - k, err := api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - if k.Name() != "foo" { - t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) - } - - verifyIPNSPath(t, k.Path().String()) -} - -func verifyIPNSPath(t *testing.T, p string) bool { - t.Helper() - if !strings.HasPrefix(p, "/ipns/") { - t.Errorf("path %q does not look like an IPNS path", p) - return false - } - k := p[len("/ipns/"):] - c, err := cid.Decode(k) - if err != nil { - t.Errorf("failed to decode IPNS key %q (%v)", k, err) - return false - } - b36, err := c.StringOfBase(mbase.Base36) - if err != nil { - t.Fatalf("cid cannot format itself in b36") - return false - } - if b36 != k { - t.Errorf("IPNS key is not base36") - } - return true -} - -func (tp *TestSuite) TestGenerateSize(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - k, err := api.Key().Generate(ctx, "foo", opt.Key.Size(2048)) - if err != nil { - t.Fatal(err) - return - } - - if k.Name() != "foo" { - t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) - } - - verifyIPNSPath(t, k.Path().String()) -} - -func (tp *TestSuite) TestGenerateType(t *testing.T) { - t.Skip("disabled until libp2p/specs#111 is fixed") - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - k, err := api.Key().Generate(ctx, "bar", opt.Key.Type(opt.Ed25519Key)) - if err != nil { - t.Fatal(err) - return - } - - if k.Name() != "bar" { - t.Errorf("expected the key to be called 'foo', got '%s'", k.Name()) - } - - // Expected to be an inlined identity hash. - if !strings.HasPrefix(k.Path().String(), "/ipns/12") { - t.Errorf("expected the key to be prefixed with '/ipns/12', got '%s'", k.Path().String()) - } -} - -func (tp *TestSuite) TestGenerateExisting(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - _, err = api.Key().Generate(ctx, "foo") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "key with name 'foo' already exists") { - t.Fatalf("expected error 'key with name 'foo' already exists', got '%s'", err.Error()) - } - } - - _, err = api.Key().Generate(ctx, "self") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot create key with name 'self'") { - t.Fatalf("expected error 'cannot create key with name 'self'', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestList(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - l, err := api.Key().List(ctx) - if err != nil { - t.Fatal(err) - return - } - - if len(l) != 2 { - t.Fatalf("expected to get 2 keys, got %d", len(l)) - return - } - - if l[0].Name() != "self" { - t.Fatalf("expected key 0 to be called 'self', got '%s'", l[0].Name()) - return - } - - if l[1].Name() != "foo" { - t.Fatalf("expected key 1 to be called 'foo', got '%s'", l[1].Name()) - return - } - - verifyIPNSPath(t, l[0].Path().String()) - verifyIPNSPath(t, l[1].Path().String()) -} - -func (tp *TestSuite) TestRename(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - k, overwrote, err := api.Key().Rename(ctx, "foo", "bar") - if err != nil { - t.Fatal(err) - return - } - - if overwrote { - t.Error("overwrote should be false") - } - - if k.Name() != "bar" { - t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) - } -} - -func (tp *TestSuite) TestRenameToSelf(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - _, _, err = api.Key().Rename(ctx, "foo", "self") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") { - t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestRenameToSelfForce(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - _, _, err = api.Key().Rename(ctx, "foo", "self", opt.Key.Force(true)) - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "cannot overwrite key with name 'self'") { - t.Fatalf("expected error 'cannot overwrite key with name 'self'', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestRenameOverwriteNoForce(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - _, err = api.Key().Generate(ctx, "bar") - if err != nil { - t.Fatal(err) - return - } - - _, _, err = api.Key().Rename(ctx, "foo", "bar") - if err == nil { - t.Error("expected error to not be nil") - } else { - if !strings.Contains(err.Error(), "key by that name already exists, refusing to overwrite") { - t.Fatalf("expected error 'key by that name already exists, refusing to overwrite', got '%s'", err.Error()) - } - } -} - -func (tp *TestSuite) TestRenameOverwrite(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - kfoo, err := api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - _, err = api.Key().Generate(ctx, "bar") - if err != nil { - t.Fatal(err) - return - } - - k, overwrote, err := api.Key().Rename(ctx, "foo", "bar", opt.Key.Force(true)) - if err != nil { - t.Fatal(err) - return - } - - if !overwrote { - t.Error("overwrote should be true") - } - - if k.Name() != "bar" { - t.Errorf("returned key should be called 'bar', got '%s'", k.Name()) - } - - if k.Path().String() != kfoo.Path().String() { - t.Errorf("k and kfoo should have equal paths, '%s'!='%s'", k.Path().String(), kfoo.Path().String()) - } -} - -func (tp *TestSuite) TestRenameSameNameNoForce(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - k, overwrote, err := api.Key().Rename(ctx, "foo", "foo") - if err != nil { - t.Fatal(err) - return - } - - if overwrote { - t.Error("overwrote should be false") - } - - if k.Name() != "foo" { - t.Errorf("returned key should be called 'foo', got '%s'", k.Name()) - } -} - -func (tp *TestSuite) TestRenameSameName(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - k, overwrote, err := api.Key().Rename(ctx, "foo", "foo", opt.Key.Force(true)) - if err != nil { - t.Fatal(err) - return - } - - if overwrote { - t.Error("overwrote should be false") - } - - if k.Name() != "foo" { - t.Errorf("returned key should be called 'foo', got '%s'", k.Name()) - } -} - -func (tp *TestSuite) TestRemove(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - k, err := api.Key().Generate(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - l, err := api.Key().List(ctx) - if err != nil { - t.Fatal(err) - return - } - - if len(l) != 2 { - t.Fatalf("expected to get 2 keys, got %d", len(l)) - return - } - - p, err := api.Key().Remove(ctx, "foo") - if err != nil { - t.Fatal(err) - return - } - - if k.Path().String() != p.Path().String() { - t.Errorf("k and p should have equal paths, '%s'!='%s'", k.Path().String(), p.Path().String()) - } - - l, err = api.Key().List(ctx) - if err != nil { - t.Fatal(err) - return - } - - if len(l) != 1 { - t.Fatalf("expected to get 1 key, got %d", len(l)) - return - } - - if l[0].Name() != "self" { - t.Errorf("expected the key to be called 'self', got '%s'", l[0].Name()) - } -} diff --git a/coreiface/tests/name.go b/coreiface/tests/name.go deleted file mode 100644 index 2b6b7ec49..000000000 --- a/coreiface/tests/name.go +++ /dev/null @@ -1,168 +0,0 @@ -package tests - -import ( - "context" - "io" - "math/rand" - "testing" - "time" - - coreiface "github.com/ipfs/boxo/coreiface" - opt "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/files" - "github.com/ipfs/boxo/ipns" - "github.com/ipfs/boxo/path" - "github.com/stretchr/testify/require" -) - -func (tp *TestSuite) TestName(t *testing.T) { - tp.hasApi(t, func(api coreiface.CoreAPI) error { - if api.Name() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestPublishResolve", tp.TestPublishResolve) - t.Run("TestBasicPublishResolveKey", tp.TestBasicPublishResolveKey) - t.Run("TestBasicPublishResolveTimeout", tp.TestBasicPublishResolveTimeout) -} - -var rnd = rand.New(rand.NewSource(0x62796532303137)) - -func addTestObject(ctx context.Context, api coreiface.CoreAPI) (path.Path, error) { - return api.Unixfs().Add(ctx, files.NewReaderFile(&io.LimitedReader{R: rnd, N: 4092})) -} - -func (tp *TestSuite) TestPublishResolve(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - init := func() (coreiface.CoreAPI, path.Path) { - apis, err := tp.MakeAPISwarm(t, ctx, 5) - require.NoError(t, err) - api := apis[0] - - p, err := addTestObject(ctx, api) - require.NoError(t, err) - return api, p - } - run := func(t *testing.T, ropts []opt.NameResolveOption) { - t.Run("basic", func(t *testing.T) { - api, p := init() - name, err := api.Name().Publish(ctx, p) - require.NoError(t, err) - - self, err := api.Key().Self(ctx) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) - - resPath, err := api.Name().Resolve(ctx, name.String(), ropts...) - require.NoError(t, err) - require.Equal(t, p.String(), resPath.String()) - }) - - t.Run("publishPath", func(t *testing.T) { - api, p := init() - p, err := path.Join(p, "/test") - require.NoError(t, err) - - name, err := api.Name().Publish(ctx, p) - require.NoError(t, err) - - self, err := api.Key().Self(ctx) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) - - resPath, err := api.Name().Resolve(ctx, name.String(), ropts...) - require.NoError(t, err) - require.Equal(t, p.String(), resPath.String()) - }) - - t.Run("revolvePath", func(t *testing.T) { - api, p := init() - name, err := api.Name().Publish(ctx, p) - require.NoError(t, err) - - self, err := api.Key().Self(ctx) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) - - resPath, err := api.Name().Resolve(ctx, name.String()+"/test", ropts...) - require.NoError(t, err) - require.Equal(t, p.String()+"/test", resPath.String()) - }) - - t.Run("publishRevolvePath", func(t *testing.T) { - api, p := init() - p, err := path.Join(p, "/a") - require.NoError(t, err) - - name, err := api.Name().Publish(ctx, p) - require.NoError(t, err) - - self, err := api.Key().Self(ctx) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) - - resPath, err := api.Name().Resolve(ctx, name.String()+"/b", ropts...) - require.NoError(t, err) - require.Equal(t, p.String()+"/b", resPath.String()) - }) - } - - t.Run("default", func(t *testing.T) { - run(t, []opt.NameResolveOption{}) - }) - - t.Run("nocache", func(t *testing.T) { - run(t, []opt.NameResolveOption{opt.Name.Cache(false)}) - }) -} - -func (tp *TestSuite) TestBasicPublishResolveKey(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 5) - require.NoError(t, err) - api := apis[0] - - k, err := api.Key().Generate(ctx, "foo") - require.NoError(t, err) - - p, err := addTestObject(ctx, api) - require.NoError(t, err) - - name, err := api.Name().Publish(ctx, p, opt.Name.Key(k.Name())) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(k.ID()).String()) - - resPath, err := api.Name().Resolve(ctx, name.String()) - require.NoError(t, err) - require.Equal(t, p.String(), resPath.String()) -} - -func (tp *TestSuite) TestBasicPublishResolveTimeout(t *testing.T) { - t.Skip("ValidTime doesn't appear to work at this time resolution") - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 5) - require.NoError(t, err) - api := apis[0] - p, err := addTestObject(ctx, api) - require.NoError(t, err) - - self, err := api.Key().Self(ctx) - require.NoError(t, err) - - name, err := api.Name().Publish(ctx, p, opt.Name.ValidTime(time.Millisecond*100)) - require.NoError(t, err) - require.Equal(t, name.String(), ipns.NameFromPeer(self.ID()).String()) - - time.Sleep(time.Second) - - _, err = api.Name().Resolve(ctx, name.String()) - require.NoError(t, err) -} - -// TODO: When swarm api is created, add multinode tests diff --git a/coreiface/tests/object.go b/coreiface/tests/object.go deleted file mode 100644 index 5c6ba828c..000000000 --- a/coreiface/tests/object.go +++ /dev/null @@ -1,467 +0,0 @@ -package tests - -import ( - "bytes" - "context" - "encoding/hex" - "io" - "strings" - "testing" - - iface "github.com/ipfs/boxo/coreiface" - opt "github.com/ipfs/boxo/coreiface/options" -) - -func (tp *TestSuite) TestObject(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.Object() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestNew", tp.TestNew) - t.Run("TestObjectPut", tp.TestObjectPut) - t.Run("TestObjectGet", tp.TestObjectGet) - t.Run("TestObjectData", tp.TestObjectData) - t.Run("TestObjectLinks", tp.TestObjectLinks) - t.Run("TestObjectStat", tp.TestObjectStat) - t.Run("TestObjectAddLink", tp.TestObjectAddLink) - t.Run("TestObjectAddLinkCreate", tp.TestObjectAddLinkCreate) - t.Run("TestObjectRmLink", tp.TestObjectRmLink) - t.Run("TestObjectAddData", tp.TestObjectAddData) - t.Run("TestObjectSetData", tp.TestObjectSetData) - t.Run("TestDiffTest", tp.TestDiffTest) -} - -func (tp *TestSuite) TestNew(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - emptyNode, err := api.Object().New(ctx) - if err != nil { - t.Fatal(err) - } - - dirNode, err := api.Object().New(ctx, opt.Object.Type("unixfs-dir")) - if err != nil { - t.Fatal(err) - } - - if emptyNode.String() != "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" { - t.Errorf("Unexpected emptyNode path: %s", emptyNode.String()) - } - - if dirNode.String() != "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" { - t.Errorf("Unexpected dirNode path: %s", dirNode.String()) - } -} - -func (tp *TestSuite) TestObjectPut(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"YmFy"}`), opt.Object.DataType("base64")) // bar - if err != nil { - t.Fatal(err) - } - - pbBytes, err := hex.DecodeString("0a0362617a") - if err != nil { - t.Fatal(err) - } - - p3, err := api.Object().Put(ctx, bytes.NewReader(pbBytes), opt.Object.InputEnc("protobuf")) - if err != nil { - t.Fatal(err) - } - - if p1.String() != "/ipfs/QmQeGyS87nyijii7kFt1zbe4n2PsXTFimzsdxyE9qh9TST" { - t.Errorf("unexpected path: %s", p1.String()) - } - - if p2.String() != "/ipfs/QmNeYRbCibmaMMK6Du6ChfServcLqFvLJF76PzzF76SPrZ" { - t.Errorf("unexpected path: %s", p2.String()) - } - - if p3.String() != "/ipfs/QmZreR7M2t7bFXAdb1V5FtQhjk4t36GnrvueLJowJbQM9m" { - t.Errorf("unexpected path: %s", p3.String()) - } -} - -func (tp *TestSuite) TestObjectGet(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - nd, err := api.Object().Get(ctx, p1) - if err != nil { - t.Fatal(err) - } - - if string(nd.RawData()[len(nd.RawData())-3:]) != "foo" { - t.Fatal("got non-matching data") - } -} - -func (tp *TestSuite) TestObjectData(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - r, err := api.Object().Data(ctx, p1) - if err != nil { - t.Fatal(err) - } - - data, err := io.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if string(data) != "foo" { - t.Fatal("got non-matching data") - } -} - -func (tp *TestSuite) TestObjectLinks(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`"}]}`)) - if err != nil { - t.Fatal(err) - } - - links, err := api.Object().Links(ctx, p2) - if err != nil { - t.Fatal(err) - } - - if len(links) != 1 { - t.Errorf("unexpected number of links: %d", len(links)) - } - - if links[0].Cid.String() != p1.RootCid().String() { - t.Fatal("cids didn't batch") - } - - if links[0].Name != "bar" { - t.Fatal("unexpected link name") - } -} - -func (tp *TestSuite) TestObjectStat(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`)) - if err != nil { - t.Fatal(err) - } - - stat, err := api.Object().Stat(ctx, p2) - if err != nil { - t.Fatal(err) - } - - if stat.Cid.String() != p2.RootCid().String() { - t.Error("unexpected stat.Cid") - } - - if stat.NumLinks != 1 { - t.Errorf("unexpected stat.NumLinks") - } - - if stat.BlockSize != 51 { - t.Error("unexpected stat.BlockSize") - } - - if stat.LinksSize != 47 { - t.Errorf("unexpected stat.LinksSize: %d", stat.LinksSize) - } - - if stat.DataSize != 4 { - t.Error("unexpected stat.DataSize") - } - - if stat.CumulativeSize != 54 { - t.Error("unexpected stat.DataSize") - } -} - -func (tp *TestSuite) TestObjectAddLink(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`)) - if err != nil { - t.Fatal(err) - } - - p3, err := api.Object().AddLink(ctx, p2, "abc", p2) - if err != nil { - t.Fatal(err) - } - - links, err := api.Object().Links(ctx, p3) - if err != nil { - t.Fatal(err) - } - - if len(links) != 2 { - t.Errorf("unexpected number of links: %d", len(links)) - } - - if links[0].Name != "abc" { - t.Errorf("unexpected link 0 name: %s", links[0].Name) - } - - if links[1].Name != "bar" { - t.Errorf("unexpected link 1 name: %s", links[1].Name) - } -} - -func (tp *TestSuite) TestObjectAddLinkCreate(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`)) - if err != nil { - t.Fatal(err) - } - - _, err = api.Object().AddLink(ctx, p2, "abc/d", p2) - if err == nil { - t.Fatal("expected an error") - } - if !strings.Contains(err.Error(), "no link by that name") { - t.Fatalf("unexpected error: %s", err.Error()) - } - - p3, err := api.Object().AddLink(ctx, p2, "abc/d", p2, opt.Object.Create(true)) - if err != nil { - t.Fatal(err) - } - - links, err := api.Object().Links(ctx, p3) - if err != nil { - t.Fatal(err) - } - - if len(links) != 2 { - t.Errorf("unexpected number of links: %d", len(links)) - } - - if links[0].Name != "abc" { - t.Errorf("unexpected link 0 name: %s", links[0].Name) - } - - if links[1].Name != "bar" { - t.Errorf("unexpected link 1 name: %s", links[1].Name) - } -} - -func (tp *TestSuite) TestObjectRmLink(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bazz", "Links":[{"Name":"bar", "Hash":"`+p1.RootCid().String()+`", "Size":3}]}`)) - if err != nil { - t.Fatal(err) - } - - p3, err := api.Object().RmLink(ctx, p2, "bar") - if err != nil { - t.Fatal(err) - } - - links, err := api.Object().Links(ctx, p3) - if err != nil { - t.Fatal(err) - } - - if len(links) != 0 { - t.Errorf("unexpected number of links: %d", len(links)) - } -} - -func (tp *TestSuite) TestObjectAddData(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().AppendData(ctx, p1, strings.NewReader("bar")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Object().Data(ctx, p2) - if err != nil { - t.Fatal(err) - } - - data, err := io.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if string(data) != "foobar" { - t.Error("unexpected data") - } -} - -func (tp *TestSuite) TestObjectSetData(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().SetData(ctx, p1, strings.NewReader("bar")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Object().Data(ctx, p2) - if err != nil { - t.Fatal(err) - } - - data, err := io.ReadAll(r) - if err != nil { - t.Fatal(err) - } - - if string(data) != "bar" { - t.Error("unexpected data") - } -} - -func (tp *TestSuite) TestDiffTest(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"foo"}`)) - if err != nil { - t.Fatal(err) - } - - p2, err := api.Object().Put(ctx, strings.NewReader(`{"Data":"bar"}`)) - if err != nil { - t.Fatal(err) - } - - changes, err := api.Object().Diff(ctx, p1, p2) - if err != nil { - t.Fatal(err) - } - - if len(changes) != 1 { - t.Fatal("unexpected changes len") - } - - if changes[0].Type != iface.DiffMod { - t.Fatal("unexpected change type") - } - - if changes[0].Before.String() != p1.String() { - t.Fatal("unexpected before path") - } - - if changes[0].After.String() != p2.String() { - t.Fatal("unexpected before path") - } -} diff --git a/coreiface/tests/path.go b/coreiface/tests/path.go deleted file mode 100644 index 116aed2e7..000000000 --- a/coreiface/tests/path.go +++ /dev/null @@ -1,148 +0,0 @@ -package tests - -import ( - "context" - "fmt" - "math" - "strings" - "testing" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - "github.com/ipfs/go-cid" - ipldcbor "github.com/ipfs/go-ipld-cbor" - "github.com/stretchr/testify/require" -) - -func newIPLDPath(t *testing.T, cid cid.Cid) path.ImmutablePath { - p, err := path.NewPath(fmt.Sprintf("/%s/%s", path.IPLDNamespace, cid.String())) - require.NoError(t, err) - im, err := path.NewImmutablePath(p) - require.NoError(t, err) - return im -} - -func (tp *TestSuite) TestPath(t *testing.T) { - t.Run("TestMutablePath", tp.TestMutablePath) - t.Run("TestPathRemainder", tp.TestPathRemainder) - t.Run("TestEmptyPathRemainder", tp.TestEmptyPathRemainder) - t.Run("TestInvalidPathRemainder", tp.TestInvalidPathRemainder) - t.Run("TestPathRoot", tp.TestPathRoot) - t.Run("TestPathJoin", tp.TestPathJoin) -} - -func (tp *TestSuite) TestMutablePath(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - require.NoError(t, err) - - blk, err := api.Block().Put(ctx, strings.NewReader(`foo`)) - require.NoError(t, err) - require.False(t, blk.Path().Mutable()) - require.NotNil(t, api.Key()) - - keys, err := api.Key().List(ctx) - require.NoError(t, err) - require.True(t, keys[0].Path().Mutable()) -} - -func (tp *TestSuite) TestPathRemainder(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - require.NoError(t, err) - require.NotNil(t, api.Dag()) - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) - require.NoError(t, err) - - err = api.Dag().Add(ctx, nd) - require.NoError(t, err) - - p, err := path.Join(path.FromCid(nd.Cid()), "foo", "bar") - require.NoError(t, err) - - _, remainder, err := api.ResolvePath(ctx, p) - require.NoError(t, err) - require.Equal(t, "/foo/bar", path.SegmentsToString(remainder...)) -} - -func (tp *TestSuite) TestEmptyPathRemainder(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - require.NoError(t, err) - require.NotNil(t, api.Dag()) - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) - require.NoError(t, err) - - err = api.Dag().Add(ctx, nd) - require.NoError(t, err) - - _, remainder, err := api.ResolvePath(ctx, path.FromCid(nd.Cid())) - require.NoError(t, err) - require.Empty(t, remainder) -} - -func (tp *TestSuite) TestInvalidPathRemainder(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - require.NoError(t, err) - require.NotNil(t, api.Dag()) - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"bar": "baz"}}`), math.MaxUint64, -1) - require.NoError(t, err) - - err = api.Dag().Add(ctx, nd) - require.NoError(t, err) - - p, err := path.Join(newIPLDPath(t, nd.Cid()), "/bar/baz") - require.NoError(t, err) - - _, _, err = api.ResolvePath(ctx, p) - require.NotNil(t, err) - require.ErrorContains(t, err, `no link named "bar"`) -} - -func (tp *TestSuite) TestPathRoot(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - api, err := tp.makeAPI(t, ctx) - require.NoError(t, err) - require.NotNil(t, api.Block()) - - blk, err := api.Block().Put(ctx, strings.NewReader(`foo`), options.Block.Format("raw")) - require.NoError(t, err) - require.NotNil(t, api.Dag()) - - nd, err := ipldcbor.FromJSON(strings.NewReader(`{"foo": {"/": "`+blk.Path().RootCid().String()+`"}}`), math.MaxUint64, -1) - require.NoError(t, err) - - err = api.Dag().Add(ctx, nd) - require.NoError(t, err) - - p, err := path.Join(newIPLDPath(t, nd.Cid()), "/foo") - require.NoError(t, err) - - rp, _, err := api.ResolvePath(ctx, p) - require.NoError(t, err) - require.Equal(t, rp.RootCid().String(), blk.Path().RootCid().String()) -} - -func (tp *TestSuite) TestPathJoin(t *testing.T) { - p1, err := path.NewPath("/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz") - require.NoError(t, err) - - p2, err := path.Join(p1, "foo") - require.NoError(t, err) - - require.Equal(t, "/ipfs/QmYNmQKp6SuaVrpgWRsPTgCQCnpxUYGq76YEKBXuj2N4H6/bar/baz/foo", p2.String()) -} diff --git a/coreiface/tests/pin.go b/coreiface/tests/pin.go deleted file mode 100644 index 49499b36a..000000000 --- a/coreiface/tests/pin.go +++ /dev/null @@ -1,611 +0,0 @@ -package tests - -import ( - "context" - "math" - "strings" - "testing" - - iface "github.com/ipfs/boxo/coreiface" - opt "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - "github.com/ipfs/go-cid" - ipldcbor "github.com/ipfs/go-ipld-cbor" - ipld "github.com/ipfs/go-ipld-format" -) - -func (tp *TestSuite) TestPin(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.Pin() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestPinAdd", tp.TestPinAdd) - t.Run("TestPinSimple", tp.TestPinSimple) - t.Run("TestPinRecursive", tp.TestPinRecursive) - t.Run("TestPinLsIndirect", tp.TestPinLsIndirect) - t.Run("TestPinLsPrecedence", tp.TestPinLsPrecedence) - t.Run("TestPinIsPinned", tp.TestPinIsPinned) -} - -func (tp *TestSuite) TestPinAdd(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p, err := api.Unixfs().Add(ctx, strFile("foo")()) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, p) - if err != nil { - t.Fatal(err) - } -} - -func (tp *TestSuite) TestPinSimple(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p, err := api.Unixfs().Add(ctx, strFile("foo")()) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, p) - if err != nil { - t.Fatal(err) - } - - list, err := accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - - if len(list) != 1 { - t.Errorf("unexpected pin list len: %d", len(list)) - } - - if list[0].Path().RootCid().String() != p.RootCid().String() { - t.Error("paths don't match") - } - - if list[0].Type() != "recursive" { - t.Error("unexpected pin type") - } - - assertIsPinned(t, ctx, api, p, "recursive") - - err = api.Pin().Rm(ctx, p) - if err != nil { - t.Fatal(err) - } - - list, err = accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - - if len(list) != 0 { - t.Errorf("unexpected pin list len: %d", len(list)) - } -} - -func (tp *TestSuite) TestPinRecursive(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p0, err := api.Unixfs().Add(ctx, strFile("foo")()) - if err != nil { - t.Fatal(err) - } - - p1, err := api.Unixfs().Add(ctx, strFile("bar")()) - if err != nil { - t.Fatal(err) - } - - nd2, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p0.RootCid().String()+`"}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - nd3, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+p1.RootCid().String()+`"}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - if err := api.Dag().AddMany(ctx, []ipld.Node{nd2, nd3}); err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(nd2.Cid())) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(nd3.Cid()), opt.Pin.Recursive(false)) - if err != nil { - t.Fatal(err) - } - - list, err := accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - - if len(list) != 3 { - t.Errorf("unexpected pin list len: %d", len(list)) - } - - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) - if err != nil { - t.Fatal(err) - } - - if len(list) != 1 { - t.Errorf("unexpected pin list len: %d", len(list)) - } - - if list[0].Path().String() != path.FromCid(nd3.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd3.Cid()).String()) - } - - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) - if err != nil { - t.Fatal(err) - } - - if len(list) != 1 { - t.Errorf("unexpected pin list len: %d", len(list)) - } - - if list[0].Path().String() != path.FromCid(nd2.Cid()).String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().String(), path.FromCid(nd2.Cid()).String()) - } - - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) - if err != nil { - t.Fatal(err) - } - - if len(list) != 1 { - t.Errorf("unexpected pin list len: %d", len(list)) - } - - if list[0].Path().RootCid().String() != p0.RootCid().String() { - t.Errorf("unexpected path, %s != %s", list[0].Path().RootCid().String(), p0.RootCid().String()) - } - - res, err := api.Pin().Verify(ctx) - if err != nil { - t.Fatal(err) - } - n := 0 - for r := range res { - if err := r.Err(); err != nil { - t.Error(err) - } - if !r.Ok() { - t.Error("expected pin to be ok") - } - n++ - } - - if n != 1 { - t.Errorf("unexpected verify result count: %d", n) - } - - // TODO: figure out a way to test verify without touching IpfsNode - /* - err = api.Block().Rm(ctx, p0, opt.Block.Force(true)) - if err != nil { - t.Fatal(err) - } - - res, err = api.Pin().Verify(ctx) - if err != nil { - t.Fatal(err) - } - n = 0 - for r := range res { - if r.Ok() { - t.Error("expected pin to not be ok") - } - - if len(r.BadNodes()) != 1 { - t.Fatalf("unexpected badNodes len") - } - - if r.BadNodes()[0].Path().Cid().String() != p0.Cid().String() { - t.Error("unexpected badNode path") - } - - if r.BadNodes()[0].Err().Error() != "merkledag: not found" { - t.Errorf("unexpected badNode error: %s", r.BadNodes()[0].Err().Error()) - } - n++ - } - - if n != 1 { - t.Errorf("unexpected verify result count: %d", n) - } - */ -} - -// TestPinLsIndirect verifies that indirect nodes are listed by pin ls even if a parent node is directly pinned -func (tp *TestSuite) TestPinLsIndirect(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foo") - - err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) - if err != nil { - t.Fatal(err) - } - - assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) -} - -// TestPinLsPrecedence verifies the precedence of pins (recursive > direct > indirect) -func (tp *TestSuite) TestPinLsPrecedence(t *testing.T) { - // Testing precedence of recursive, direct and indirect pins - // Results should be recursive > indirect, direct > indirect, and recursive > direct - - t.Run("TestPinLsPredenceRecursiveIndirect", tp.TestPinLsPredenceRecursiveIndirect) - t.Run("TestPinLsPrecedenceDirectIndirect", tp.TestPinLsPrecedenceDirectIndirect) - t.Run("TestPinLsPrecedenceRecursiveDirect", tp.TestPinLsPrecedenceRecursiveDirect) -} - -func (tp *TestSuite) TestPinLsPredenceRecursiveIndirect(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - // Test recursive > indirect - leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive > indirect") - - err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(parent.Cid())) - if err != nil { - t.Fatal(err) - } - - assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) -} - -func (tp *TestSuite) TestPinLsPrecedenceDirectIndirect(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - // Test direct > indirect - leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "direct > indirect") - - err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) - if err != nil { - t.Fatal(err) - } - - assertPinTypes(t, ctx, api, []cidContainer{grandparent}, []cidContainer{parent}, []cidContainer{leaf}) -} - -func (tp *TestSuite) TestPinLsPrecedenceRecursiveDirect(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - // Test recursive > direct - leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "recursive + direct = error") - - err = api.Pin().Add(ctx, path.FromCid(parent.Cid())) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(parent.Cid()), opt.Pin.Recursive(false)) - if err == nil { - t.Fatal("expected error directly pinning a recursively pinned node") - } - - assertPinTypes(t, ctx, api, []cidContainer{parent}, []cidContainer{}, []cidContainer{leaf}) - - err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid()), opt.Pin.Recursive(false)) - if err != nil { - t.Fatal(err) - } - - err = api.Pin().Add(ctx, path.FromCid(grandparent.Cid())) - if err != nil { - t.Fatal(err) - } - - assertPinTypes(t, ctx, api, []cidContainer{grandparent, parent}, []cidContainer{}, []cidContainer{leaf}) -} - -func (tp *TestSuite) TestPinIsPinned(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - leaf, parent, grandparent := getThreeChainedNodes(t, ctx, api, "foofoo") - - assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid())) - assertNotPinned(t, ctx, api, newIPLDPath(t, parent.Cid())) - assertNotPinned(t, ctx, api, newIPLDPath(t, leaf.Cid())) - - err = api.Pin().Add(ctx, newIPLDPath(t, parent.Cid()), opt.Pin.Recursive(true)) - if err != nil { - t.Fatal(err) - } - - assertNotPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid())) - assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive") - assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect") - - err = api.Pin().Add(ctx, newIPLDPath(t, grandparent.Cid()), opt.Pin.Recursive(false)) - if err != nil { - t.Fatal(err) - } - - assertIsPinned(t, ctx, api, newIPLDPath(t, grandparent.Cid()), "direct") - assertIsPinned(t, ctx, api, newIPLDPath(t, parent.Cid()), "recursive") - assertIsPinned(t, ctx, api, newIPLDPath(t, leaf.Cid()), "indirect") -} - -type cidContainer interface { - Cid() cid.Cid -} - -type immutablePathCidContainer struct { - path.ImmutablePath -} - -func (i immutablePathCidContainer) Cid() cid.Cid { - return i.RootCid() -} - -func getThreeChainedNodes(t *testing.T, ctx context.Context, api iface.CoreAPI, leafData string) (cidContainer, cidContainer, cidContainer) { - leaf, err := api.Unixfs().Add(ctx, strFile(leafData)()) - if err != nil { - t.Fatal(err) - } - - parent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+leaf.RootCid().String()+`"}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - grandparent, err := ipldcbor.FromJSON(strings.NewReader(`{"lnk": {"/": "`+parent.Cid().String()+`"}}`), math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - if err := api.Dag().AddMany(ctx, []ipld.Node{parent, grandparent}); err != nil { - t.Fatal(err) - } - - return immutablePathCidContainer{leaf}, parent, grandparent -} - -func assertPinTypes(t *testing.T, ctx context.Context, api iface.CoreAPI, recusive, direct, indirect []cidContainer) { - assertPinLsAllConsistency(t, ctx, api) - - list, err := accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Recursive())) - if err != nil { - t.Fatal(err) - } - - assertPinCids(t, list, recusive...) - - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Direct())) - if err != nil { - t.Fatal(err) - } - - assertPinCids(t, list, direct...) - - list, err = accPins(api.Pin().Ls(ctx, opt.Pin.Ls.Indirect())) - if err != nil { - t.Fatal(err) - } - - assertPinCids(t, list, indirect...) -} - -// assertPinCids verifies that the pins match the expected cids -func assertPinCids(t *testing.T, pins []iface.Pin, cids ...cidContainer) { - t.Helper() - - if expected, actual := len(cids), len(pins); expected != actual { - t.Fatalf("expected pin list to have len %d, was %d", expected, actual) - } - - cSet := cid.NewSet() - for _, c := range cids { - cSet.Add(c.Cid()) - } - - valid := true - for _, p := range pins { - c := p.Path().RootCid() - if cSet.Has(c) { - cSet.Remove(c) - } else { - valid = false - break - } - } - - valid = valid && cSet.Len() == 0 - - if !valid { - pinStrs := make([]string, len(pins)) - for i, p := range pins { - pinStrs[i] = p.Path().RootCid().String() - } - pathStrs := make([]string, len(cids)) - for i, c := range cids { - pathStrs[i] = c.Cid().String() - } - t.Fatalf("expected: %s \nactual: %s", strings.Join(pathStrs, ", "), strings.Join(pinStrs, ", ")) - } -} - -// assertPinLsAllConsistency verifies that listing all pins gives the same result as listing the pin types individually -func assertPinLsAllConsistency(t *testing.T, ctx context.Context, api iface.CoreAPI) { - t.Helper() - allPins, err := accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - - type pinTypeProps struct { - *cid.Set - opt.PinLsOption - } - - all, recursive, direct, indirect := cid.NewSet(), cid.NewSet(), cid.NewSet(), cid.NewSet() - typeMap := map[string]*pinTypeProps{ - "recursive": {recursive, opt.Pin.Ls.Recursive()}, - "direct": {direct, opt.Pin.Ls.Direct()}, - "indirect": {indirect, opt.Pin.Ls.Indirect()}, - } - - for _, p := range allPins { - if !all.Visit(p.Path().RootCid()) { - t.Fatalf("pin ls returned the same cid multiple times") - } - - typeStr := p.Type() - if typeSet, ok := typeMap[p.Type()]; ok { - typeSet.Add(p.Path().RootCid()) - } else { - t.Fatalf("unknown pin type: %s", typeStr) - } - } - - for typeStr, pinProps := range typeMap { - pins, err := accPins(api.Pin().Ls(ctx, pinProps.PinLsOption)) - if err != nil { - t.Fatal(err) - } - - if expected, actual := len(pins), pinProps.Set.Len(); expected != actual { - t.Fatalf("pin ls all has %d pins of type %s, but pin ls for the type has %d", expected, typeStr, actual) - } - - for _, p := range pins { - if pinType := p.Type(); pinType != typeStr { - t.Fatalf("returned wrong pin type: expected %s, got %s", typeStr, pinType) - } - - if c := p.Path().RootCid(); !pinProps.Has(c) { - t.Fatalf("%s expected to be in pin ls all as type %s", c.String(), typeStr) - } - } - } -} - -func assertIsPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path, typeStr string) { - t.Helper() - withType, err := opt.Pin.IsPinned.Type(typeStr) - if err != nil { - t.Fatal("unhandled pin type") - } - - whyPinned, pinned, err := api.Pin().IsPinned(ctx, p, withType) - if err != nil { - t.Fatal(err) - } - - if !pinned { - t.Fatalf("%s expected to be pinned with type %s", p, typeStr) - } - - switch typeStr { - case "recursive", "direct": - if typeStr != whyPinned { - t.Fatalf("reason for pinning expected to be %s for %s, got %s", typeStr, p, whyPinned) - } - case "indirect": - if whyPinned == "" { - t.Fatalf("expected to have a pin reason for %s", p) - } - } -} - -func assertNotPinned(t *testing.T, ctx context.Context, api iface.CoreAPI, p path.Path) { - t.Helper() - - _, pinned, err := api.Pin().IsPinned(ctx, p) - if err != nil { - t.Fatal(err) - } - - if pinned { - t.Fatalf("%s expected to not be pinned", p) - } -} - -func accPins(pins <-chan iface.Pin, err error) ([]iface.Pin, error) { - if err != nil { - return nil, err - } - - var result []iface.Pin - - for pin := range pins { - if pin.Err() != nil { - return nil, pin.Err() - } - result = append(result, pin) - } - - return result, nil -} diff --git a/coreiface/tests/pubsub.go b/coreiface/tests/pubsub.go deleted file mode 100644 index 8cbc6a3eb..000000000 --- a/coreiface/tests/pubsub.go +++ /dev/null @@ -1,136 +0,0 @@ -package tests - -import ( - "context" - "testing" - "time" - - iface "github.com/ipfs/boxo/coreiface" - "github.com/ipfs/boxo/coreiface/options" -) - -func (tp *TestSuite) TestPubSub(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.PubSub() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestBasicPubSub", tp.TestBasicPubSub) -} - -func (tp *TestSuite) TestBasicPubSub(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - apis, err := tp.MakeAPISwarm(t, ctx, 2) - if err != nil { - t.Fatal(err) - } - - sub, err := apis[0].PubSub().Subscribe(ctx, "testch") - if err != nil { - t.Fatal(err) - } - - done := make(chan struct{}) - go func() { - defer close(done) - - ticker := time.NewTicker(100 * time.Millisecond) - defer ticker.Stop() - - for { - err := apis[1].PubSub().Publish(ctx, "testch", []byte("hello world")) - switch err { - case nil: - case context.Canceled: - return - default: - t.Error(err) - cancel() - return - } - select { - case <-ticker.C: - case <-ctx.Done(): - return - } - } - }() - - // Wait for the sender to finish before we return. - // Otherwise, we can get random errors as publish fails. - defer func() { - cancel() - <-done - }() - - m, err := sub.Next(ctx) - if err != nil { - t.Fatal(err) - } - - if string(m.Data()) != "hello world" { - t.Errorf("got invalid data: %s", string(m.Data())) - } - - self1, err := apis[1].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - if m.From() != self1.ID() { - t.Errorf("m.From didn't match") - } - - peers, err := apis[1].PubSub().Peers(ctx, options.PubSub.Topic("testch")) - if err != nil { - t.Fatal(err) - } - - if len(peers) != 1 { - t.Fatalf("got incorrect number of peers: %d", len(peers)) - } - - self0, err := apis[0].Key().Self(ctx) - if err != nil { - t.Fatal(err) - } - - if peers[0] != self0.ID() { - t.Errorf("peer didn't match") - } - - peers, err = apis[1].PubSub().Peers(ctx, options.PubSub.Topic("nottestch")) - if err != nil { - t.Fatal(err) - } - - if len(peers) != 0 { - t.Fatalf("got incorrect number of peers: %d", len(peers)) - } - - topics, err := apis[0].PubSub().Ls(ctx) - if err != nil { - t.Fatal(err) - } - - if len(topics) != 1 { - t.Fatalf("got incorrect number of topics: %d", len(peers)) - } - - if topics[0] != "testch" { - t.Errorf("topic didn't match") - } - - topics, err = apis[1].PubSub().Ls(ctx) - if err != nil { - t.Fatal(err) - } - - if len(topics) != 0 { - t.Fatalf("got incorrect number of topics: %d", len(peers)) - } -} diff --git a/coreiface/tests/routing.go b/coreiface/tests/routing.go deleted file mode 100644 index c56e91659..000000000 --- a/coreiface/tests/routing.go +++ /dev/null @@ -1,100 +0,0 @@ -package tests - -import ( - "context" - "testing" - "time" - - iface "github.com/ipfs/boxo/coreiface" - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/ipns" - "github.com/ipfs/boxo/path" - "github.com/stretchr/testify/require" -) - -func (tp *TestSuite) TestRouting(t *testing.T) { - tp.hasApi(t, func(api iface.CoreAPI) error { - if api.Routing() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestRoutingGet", tp.TestRoutingGet) - t.Run("TestRoutingPut", tp.TestRoutingPut) - t.Run("TestRoutingPutOffline", tp.TestRoutingPutOffline) -} - -func (tp *TestSuite) testRoutingPublishKey(t *testing.T, ctx context.Context, api iface.CoreAPI, opts ...options.NamePublishOption) (path.Path, ipns.Name) { - p, err := addTestObject(ctx, api) - require.NoError(t, err) - - name, err := api.Name().Publish(ctx, p, opts...) - require.NoError(t, err) - - time.Sleep(3 * time.Second) - return p, name -} - -func (tp *TestSuite) TestRoutingGet(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - apis, err := tp.MakeAPISwarm(t, ctx, 2) - require.NoError(t, err) - - // Node 1: publishes an IPNS name - p, name := tp.testRoutingPublishKey(t, ctx, apis[0]) - - // Node 2: retrieves the best value for the IPNS name. - data, err := apis[1].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) - require.NoError(t, err) - - rec, err := ipns.UnmarshalRecord(data) - require.NoError(t, err) - - val, err := rec.Value() - require.NoError(t, err) - require.Equal(t, p.String(), val.String()) -} - -func (tp *TestSuite) TestRoutingPut(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - apis, err := tp.MakeAPISwarm(t, ctx, 2) - require.NoError(t, err) - - // Create and publish IPNS entry. - _, name := tp.testRoutingPublishKey(t, ctx, apis[0]) - - // Get valid routing value. - data, err := apis[0].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) - require.NoError(t, err) - - // Put routing value. - err = apis[1].Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data) - require.NoError(t, err) -} - -func (tp *TestSuite) TestRoutingPutOffline(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // init a swarm & publish an IPNS entry to get a valid payload - apis, err := tp.MakeAPISwarm(t, ctx, 2) - require.NoError(t, err) - - _, name := tp.testRoutingPublishKey(t, ctx, apis[0], options.Name.AllowOffline(true)) - data, err := apis[0].Routing().Get(ctx, ipns.NamespacePrefix+name.String()) - require.NoError(t, err) - - // init our offline node and try to put the payload - api, err := tp.makeAPIWithIdentityAndOffline(t, ctx) - require.NoError(t, err) - - err = api.Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data) - require.Error(t, err, "this operation should fail because we are offline") - - err = api.Routing().Put(ctx, ipns.NamespacePrefix+name.String(), data, options.Put.AllowOffline(true)) - require.NoError(t, err) -} diff --git a/coreiface/tests/unixfs.go b/coreiface/tests/unixfs.go deleted file mode 100644 index 31ac1b5c9..000000000 --- a/coreiface/tests/unixfs.go +++ /dev/null @@ -1,1084 +0,0 @@ -package tests - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "io" - "math" - "math/rand" - "os" - "strconv" - "strings" - "sync" - "testing" - - coreiface "github.com/ipfs/boxo/coreiface" - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/path" - - "github.com/ipfs/boxo/files" - mdag "github.com/ipfs/boxo/ipld/merkledag" - "github.com/ipfs/boxo/ipld/unixfs" - "github.com/ipfs/boxo/ipld/unixfs/importer/helpers" - "github.com/ipfs/go-cid" - cbor "github.com/ipfs/go-ipld-cbor" - ipld "github.com/ipfs/go-ipld-format" - mh "github.com/multiformats/go-multihash" -) - -func (tp *TestSuite) TestUnixfs(t *testing.T) { - tp.hasApi(t, func(api coreiface.CoreAPI) error { - if api.Unixfs() == nil { - return errAPINotImplemented - } - return nil - }) - - t.Run("TestAdd", tp.TestAdd) - t.Run("TestAddPinned", tp.TestAddPinned) - t.Run("TestAddHashOnly", tp.TestAddHashOnly) - t.Run("TestGetEmptyFile", tp.TestGetEmptyFile) - t.Run("TestGetDir", tp.TestGetDir) - t.Run("TestGetNonUnixfs", tp.TestGetNonUnixfs) - t.Run("TestLs", tp.TestLs) - t.Run("TestEntriesExpired", tp.TestEntriesExpired) - t.Run("TestLsEmptyDir", tp.TestLsEmptyDir) - t.Run("TestLsNonUnixfs", tp.TestLsNonUnixfs) - t.Run("TestAddCloses", tp.TestAddCloses) - t.Run("TestGetSeek", tp.TestGetSeek) - t.Run("TestGetReadAt", tp.TestGetReadAt) -} - -// `echo -n 'hello, world!' | ipfs add` -var ( - hello = "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" - helloStr = "hello, world!" -) - -// `echo -n | ipfs add` -var emptyFile = "/ipfs/QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH" - -func strFile(data string) func() files.Node { - return func() files.Node { - return files.NewBytesFile([]byte(data)) - } -} - -func twoLevelDir() func() files.Node { - return func() files.Node { - return files.NewMapDirectory(map[string]files.Node{ - "abc": files.NewMapDirectory(map[string]files.Node{ - "def": files.NewBytesFile([]byte("world")), - }), - - "bar": files.NewBytesFile([]byte("hello2")), - "foo": files.NewBytesFile([]byte("hello1")), - }) - } -} - -func flatDir() files.Node { - return files.NewMapDirectory(map[string]files.Node{ - "bar": files.NewBytesFile([]byte("hello2")), - "foo": files.NewBytesFile([]byte("hello1")), - }) -} - -func wrapped(names ...string) func(f files.Node) files.Node { - return func(f files.Node) files.Node { - for i := range names { - f = files.NewMapDirectory(map[string]files.Node{ - names[len(names)-i-1]: f, - }) - } - return f - } -} - -func (tp *TestSuite) TestAdd(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p := func(h string) path.ImmutablePath { - c, err := cid.Parse(h) - if err != nil { - t.Fatal(err) - } - return path.FromCid(c) - } - - rf, err := os.CreateTemp(os.TempDir(), "unixfs-add-real") - if err != nil { - t.Fatal(err) - } - rfp := rf.Name() - - if _, err := rf.Write([]byte(helloStr)); err != nil { - t.Fatal(err) - } - - stat, err := rf.Stat() - if err != nil { - t.Fatal(err) - } - - if err := rf.Close(); err != nil { - t.Fatal(err) - } - defer os.Remove(rfp) - - realFile := func() files.Node { - n, err := files.NewReaderPathFile(rfp, io.NopCloser(strings.NewReader(helloStr)), stat) - if err != nil { - t.Fatal(err) - } - return n - } - - cases := []struct { - name string - data func() files.Node - expect func(files.Node) files.Node - - apiOpts []options.ApiOption - - path string - err string - - wrap string - - events []coreiface.AddEvent - - opts []options.UnixfsAddOption - }{ - // Simple cases - { - name: "simpleAdd", - data: strFile(helloStr), - path: hello, - opts: []options.UnixfsAddOption{}, - }, - { - name: "addEmpty", - data: strFile(""), - path: emptyFile, - }, - // CIDv1 version / rawLeaves - { - name: "addCidV1", - data: strFile(helloStr), - path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", - opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1)}, - }, - { - name: "addCidV1NoLeaves", - data: strFile(helloStr), - path: "/ipfs/bafybeibhbcn7k7o2m6xsqkrlfiokod3nxwe47viteynhruh6uqx7hvkjfu", - opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(1), options.Unixfs.RawLeaves(false)}, - }, - // Non sha256 hash vs CID - { - name: "addCidSha3", - data: strFile(helloStr), - path: "/ipfs/bafkrmichjflejeh6aren53o7pig7zk3m3vxqcoc2i5dv326k3x6obh7jry", - opts: []options.UnixfsAddOption{options.Unixfs.Hash(mh.SHA3_256)}, - }, - { - name: "addCidSha3Cid0", - data: strFile(helloStr), - err: "CIDv0 only supports sha2-256", - opts: []options.UnixfsAddOption{options.Unixfs.CidVersion(0), options.Unixfs.Hash(mh.SHA3_256)}, - }, - // Inline - { - name: "addInline", - data: strFile(helloStr), - path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", - opts: []options.UnixfsAddOption{options.Unixfs.Inline(true)}, - }, - { - name: "addInlineLimit", - data: strFile(helloStr), - path: "/ipfs/bafyaafikcmeaeeqnnbswy3dpfqqho33snrsccgan", - opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true)}, - }, - { - name: "addInlineZero", - data: strFile(""), - path: "/ipfs/bafkqaaa", - opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(0), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, - }, - { // TODO: after coreapi add is used in `ipfs add`, consider making this default for inline - name: "addInlineRaw", - data: strFile(helloStr), - path: "/ipfs/bafkqadlimvwgy3zmeb3w64tmmqqq", - opts: []options.UnixfsAddOption{options.Unixfs.InlineLimit(32), options.Unixfs.Inline(true), options.Unixfs.RawLeaves(true)}, - }, - // Chunker / Layout - { - name: "addChunks", - data: strFile(strings.Repeat("aoeuidhtns", 200)), - path: "/ipfs/QmRo11d4QJrST47aaiGVJYwPhoNA4ihRpJ5WaxBWjWDwbX", - opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4")}, - }, - { - name: "addChunksTrickle", - data: strFile(strings.Repeat("aoeuidhtns", 200)), - path: "/ipfs/QmNNhDGttafX3M1wKWixGre6PrLFGjnoPEDXjBYpTv93HP", - opts: []options.UnixfsAddOption{options.Unixfs.Chunker("size-4"), options.Unixfs.Layout(options.TrickleLayout)}, - }, - // Local - { - name: "addLocal", // better cases in sharness - data: strFile(helloStr), - path: hello, - apiOpts: []options.ApiOption{options.Api.Offline(true)}, - }, - { - name: "hashOnly", // test (non)fetchability - data: strFile(helloStr), - path: hello, - opts: []options.UnixfsAddOption{options.Unixfs.HashOnly(true)}, - }, - // multi file - { - name: "simpleDirNoWrap", - data: flatDir, - path: "/ipfs/QmRKGpFfR32FVXdvJiHfo4WJ5TDYBsM1P9raAp1p6APWSp", - }, - { - name: "simpleDir", - data: flatDir, - wrap: "t", - expect: wrapped("t"), - path: "/ipfs/Qmc3nGXm1HtUVCmnXLQHvWcNwfdZGpfg2SRm1CxLf7Q2Rm", - }, - { - name: "twoLevelDir", - data: twoLevelDir(), - wrap: "t", - expect: wrapped("t"), - path: "/ipfs/QmPwsL3T5sWhDmmAWZHAzyjKtMVDS9a11aHNRqb3xoVnmg", - }, - // wrapped - { - name: "addWrapped", - path: "/ipfs/QmVE9rNpj5doj7XHzp5zMUxD7BJgXEqx4pe3xZ3JBReWHE", - data: func() files.Node { - return files.NewBytesFile([]byte(helloStr)) - }, - wrap: "foo", - expect: wrapped("foo"), - }, - // hidden - { - name: "hiddenFilesAdded", - data: func() files.Node { - return files.NewMapDirectory(map[string]files.Node{ - ".bar": files.NewBytesFile([]byte("hello2")), - "bar": files.NewBytesFile([]byte("hello2")), - "foo": files.NewBytesFile([]byte("hello1")), - }) - }, - wrap: "t", - expect: wrapped("t"), - path: "/ipfs/QmPXLSBX382vJDLrGakcbrZDkU3grfkjMox7EgSC9KFbtQ", - }, - // NoCopy - { - name: "simpleNoCopy", - data: realFile, - path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", - opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, - }, - { - name: "noCopyNoRaw", - data: realFile, - path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", - opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true), options.Unixfs.RawLeaves(false)}, - err: "nocopy option requires '--raw-leaves' to be enabled as well", - }, - { - name: "noCopyNoPath", - data: strFile(helloStr), - path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", - opts: []options.UnixfsAddOption{options.Unixfs.Nocopy(true)}, - err: helpers.ErrMissingFsRef.Error(), - }, - // Events / Progress - { - name: "simpleAddEvent", - data: strFile(helloStr), - path: "/ipfs/bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", - events: []coreiface.AddEvent{ - {Name: "bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa", Path: p("bafkreidi4zlleupgp2bvrpxyja5lbvi4mym7hz5bvhyoowby2qp7g2hxfa"), Size: strconv.Itoa(len(helloStr))}, - }, - opts: []options.UnixfsAddOption{options.Unixfs.RawLeaves(true)}, - }, - { - name: "silentAddEvent", - data: twoLevelDir(), - path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", - events: []coreiface.AddEvent{ - {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, - {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, - }, - opts: []options.UnixfsAddOption{options.Unixfs.Silent(true)}, - }, - { - name: "dirAddEvents", - data: twoLevelDir(), - path: "/ipfs/QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr", - events: []coreiface.AddEvent{ - {Name: "abc/def", Path: p("QmNyJpQkU1cEkBwMDhDNFstr42q55mqG5GE5Mgwug4xyGk"), Size: "13"}, - {Name: "bar", Path: p("QmS21GuXiRMvJKHos4ZkEmQDmRBqRaF5tQS2CQCu2ne9sY"), Size: "14"}, - {Name: "foo", Path: p("QmfAjGiVpTN56TXi6SBQtstit5BEw3sijKj1Qkxn6EXKzJ"), Size: "14"}, - {Name: "abc", Path: p("QmU7nuGs2djqK99UNsNgEPGh6GV4662p6WtsgccBNGTDxt"), Size: "62"}, - {Name: "", Path: p("QmVG2ZYCkV1S4TK8URA3a4RupBF17A8yAr4FqsRDXVJASr"), Size: "229"}, - }, - }, - { - name: "progress1M", - data: func() files.Node { - return files.NewReaderFile(bytes.NewReader(bytes.Repeat([]byte{0}, 1000000))) - }, - path: "/ipfs/QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", - events: []coreiface.AddEvent{ - {Name: "", Bytes: 262144}, - {Name: "", Bytes: 524288}, - {Name: "", Bytes: 786432}, - {Name: "", Bytes: 1000000}, - {Name: "QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD", Path: p("QmXXNNbwe4zzpdMg62ZXvnX1oU7MwSrQ3vAEtuwFKCm1oD"), Size: "1000256"}, - }, - wrap: "", - opts: []options.UnixfsAddOption{options.Unixfs.Progress(true)}, - }, - } - - for _, testCase := range cases { - t.Run(testCase.name, func(t *testing.T) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // recursive logic - - data := testCase.data() - if testCase.wrap != "" { - data = files.NewMapDirectory(map[string]files.Node{ - testCase.wrap: data, - }) - } - - // handle events if relevant to test case - - opts := testCase.opts - eventOut := make(chan interface{}) - var evtWg sync.WaitGroup - if len(testCase.events) > 0 { - opts = append(opts, options.Unixfs.Events(eventOut)) - evtWg.Add(1) - - go func() { - defer evtWg.Done() - expected := testCase.events - - for evt := range eventOut { - event, ok := evt.(*coreiface.AddEvent) - if !ok { - t.Error("unexpected event type") - continue - } - - if len(expected) < 1 { - t.Error("got more events than expected") - continue - } - - if expected[0].Size != event.Size { - t.Errorf("Event.Size didn't match, %s != %s", expected[0].Size, event.Size) - } - - if expected[0].Name != event.Name { - t.Errorf("Event.Name didn't match, %s != %s", expected[0].Name, event.Name) - } - - if (expected[0].Path != path.ImmutablePath{} && event.Path != path.ImmutablePath{}) { - if expected[0].Path.RootCid().String() != event.Path.RootCid().String() { - t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) - } - } else if event.Path != expected[0].Path { - t.Errorf("Event.Hash didn't match, %s != %s", expected[0].Path, event.Path) - } - if expected[0].Bytes != event.Bytes { - t.Errorf("Event.Bytes didn't match, %d != %d", expected[0].Bytes, event.Bytes) - } - - expected = expected[1:] - } - - if len(expected) > 0 { - t.Errorf("%d event(s) didn't arrive", len(expected)) - } - }() - } - - tapi, err := api.WithOptions(testCase.apiOpts...) - if err != nil { - t.Fatal(err) - } - - // Add! - - p, err := tapi.Unixfs().Add(ctx, data, opts...) - close(eventOut) - evtWg.Wait() - if testCase.err != "" { - if err == nil { - t.Fatalf("expected an error: %s", testCase.err) - } - if err.Error() != testCase.err { - t.Fatalf("expected an error: '%s' != '%s'", err.Error(), testCase.err) - } - return - } - if err != nil { - t.Fatal(err) - } - - if p.String() != testCase.path { - t.Errorf("expected path %s, got: %s", testCase.path, p) - } - - // compare file structure with Unixfs().Get - - var cmpFile func(origName string, orig files.Node, gotName string, got files.Node) - cmpFile = func(origName string, orig files.Node, gotName string, got files.Node) { - _, origDir := orig.(files.Directory) - _, gotDir := got.(files.Directory) - - if origName != gotName { - t.Errorf("file name mismatch, orig='%s', got='%s'", origName, gotName) - } - - if origDir != gotDir { - t.Fatalf("file type mismatch on %s", origName) - } - - if !gotDir { - defer orig.Close() - defer got.Close() - - do, err := io.ReadAll(orig.(files.File)) - if err != nil { - t.Fatal(err) - } - - dg, err := io.ReadAll(got.(files.File)) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(do, dg) { - t.Fatal("data not equal") - } - - return - } - - origIt := orig.(files.Directory).Entries() - gotIt := got.(files.Directory).Entries() - - for { - if origIt.Next() { - if !gotIt.Next() { - t.Fatal("gotIt out of entries before origIt") - } - } else { - if gotIt.Next() { - t.Fatal("origIt out of entries before gotIt") - } - break - } - - cmpFile(origIt.Name(), origIt.Node(), gotIt.Name(), gotIt.Node()) - } - if origIt.Err() != nil { - t.Fatal(origIt.Err()) - } - if gotIt.Err() != nil { - t.Fatal(gotIt.Err()) - } - } - - f, err := tapi.Unixfs().Get(ctx, p) - if err != nil { - t.Fatal(err) - } - - orig := testCase.data() - if testCase.expect != nil { - orig = testCase.expect(orig) - } - - cmpFile("", orig, "", f) - }) - } -} - -func (tp *TestSuite) TestAddPinned(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.Pin(true)) - if err != nil { - t.Fatal(err) - } - - pins, err := accPins(api.Pin().Ls(ctx)) - if err != nil { - t.Fatal(err) - } - if len(pins) != 1 { - t.Fatalf("expected 1 pin, got %d", len(pins)) - } - - if pins[0].Path().String() != "/ipfs/QmQy2Dw4Wk7rdJKjThjYXzfFJNaRKRHhHP5gHHXroJMYxk" { - t.Fatalf("got unexpected pin: %s", pins[0].Path().String()) - } -} - -func (tp *TestSuite) TestAddHashOnly(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - p, err := api.Unixfs().Add(ctx, strFile(helloStr)(), options.Unixfs.HashOnly(true)) - if err != nil { - t.Fatal(err) - } - - if p.String() != hello { - t.Errorf("unxepected path: %s", p.String()) - } - - _, err = api.Block().Get(ctx, p) - if err == nil { - t.Fatal("expected an error") - } - if !ipld.IsNotFound(err) { - t.Errorf("unxepected error: %s", err.Error()) - } -} - -func (tp *TestSuite) TestGetEmptyFile(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Unixfs().Add(ctx, files.NewBytesFile([]byte{})) - if err != nil { - t.Fatal(err) - } - - emptyFilePath, err := path.NewPath(emptyFile) - if err != nil { - t.Fatal(err) - } - - r, err := api.Unixfs().Get(ctx, emptyFilePath) - if err != nil { - t.Fatal(err) - } - - buf := make([]byte, 1) // non-zero so that Read() actually tries to read - n, err := io.ReadFull(r.(files.File), buf) - if err != nil && err != io.EOF { - t.Error(err) - } - if !bytes.HasPrefix(buf, []byte{0x00}) { - t.Fatalf("expected empty data, got [%s] [read=%d]", buf, n) - } -} - -func (tp *TestSuite) TestGetDir(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - edir := unixfs.EmptyDirNode() - err = api.Dag().Add(ctx, edir) - if err != nil { - t.Fatal(err) - } - p := path.FromCid(edir.Cid()) - - emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) - if err != nil { - t.Fatal(err) - } - - if p.String() != path.FromCid(emptyDir.Cid()).String() { - t.Fatalf("expected path %s, got: %s", emptyDir.Cid(), p.String()) - } - - r, err := api.Unixfs().Get(ctx, path.FromCid(emptyDir.Cid())) - if err != nil { - t.Fatal(err) - } - - if _, ok := r.(files.Directory); !ok { - t.Fatalf("expected a directory") - } -} - -func (tp *TestSuite) TestGetNonUnixfs(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd := new(mdag.ProtoNode) - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - _, err = api.Unixfs().Get(ctx, path.FromCid(nd.Cid())) - if !strings.Contains(err.Error(), "proto: required field") { - t.Fatalf("expected protobuf error, got: %s", err) - } -} - -func (tp *TestSuite) TestLs(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - r := strings.NewReader("content-of-file") - p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ - "name-of-file": files.NewReaderFile(r), - "name-of-symlink": files.NewLinkFile("/foo/bar", nil), - })) - if err != nil { - t.Fatal(err) - } - - entries, err := api.Unixfs().Ls(ctx, p) - if err != nil { - t.Fatal(err) - } - - entry := <-entries - if entry.Err != nil { - t.Fatal(entry.Err) - } - if entry.Size != 15 { - t.Errorf("expected size = 15, got %d", entry.Size) - } - if entry.Name != "name-of-file" { - t.Errorf("expected name = name-of-file, got %s", entry.Name) - } - if entry.Type != coreiface.TFile { - t.Errorf("wrong type %s", entry.Type) - } - if entry.Cid.String() != "QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr" { - t.Errorf("expected cid = QmX3qQVKxDGz3URVC3861Z3CKtQKGBn6ffXRBBWGMFz9Lr, got %s", entry.Cid) - } - entry = <-entries - if entry.Err != nil { - t.Fatal(entry.Err) - } - if entry.Type != coreiface.TSymlink { - t.Errorf("wrong type %s", entry.Type) - } - if entry.Name != "name-of-symlink" { - t.Errorf("expected name = name-of-symlink, got %s", entry.Name) - } - if entry.Target != "/foo/bar" { - t.Errorf("expected symlink target to be /foo/bar, got %s", entry.Target) - } - - if l, ok := <-entries; ok { - t.Errorf("didn't expect a second link") - if l.Err != nil { - t.Error(l.Err) - } - } -} - -func (tp *TestSuite) TestEntriesExpired(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - r := strings.NewReader("content-of-file") - p, err := api.Unixfs().Add(ctx, files.NewMapDirectory(map[string]files.Node{ - "name-of-file": files.NewReaderFile(r), - })) - if err != nil { - t.Fatal(err) - } - - ctx, cancel = context.WithCancel(ctx) - - nd, err := api.Unixfs().Get(ctx, p) - if err != nil { - t.Fatal(err) - } - cancel() - - it := files.ToDir(nd).Entries() - if it == nil { - t.Fatal("it was nil") - } - - if it.Next() { - t.Fatal("Next succeeded") - } - - if it.Err() != context.Canceled { - t.Fatalf("unexpected error %s", it.Err()) - } - - if it.Next() { - t.Fatal("Next succeeded") - } -} - -func (tp *TestSuite) TestLsEmptyDir(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - _, err = api.Unixfs().Add(ctx, files.NewSliceDirectory([]files.DirEntry{})) - if err != nil { - t.Fatal(err) - } - - emptyDir, err := api.Object().New(ctx, options.Object.Type("unixfs-dir")) - if err != nil { - t.Fatal(err) - } - - links, err := api.Unixfs().Ls(ctx, path.FromCid(emptyDir.Cid())) - if err != nil { - t.Fatal(err) - } - - if len(links) != 0 { - t.Fatalf("expected 0 links, got %d", len(links)) - } -} - -// TODO(lgierth) this should test properly, with len(links) > 0 -func (tp *TestSuite) TestLsNonUnixfs(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - nd, err := cbor.WrapObject(map[string]interface{}{"foo": "bar"}, math.MaxUint64, -1) - if err != nil { - t.Fatal(err) - } - - err = api.Dag().Add(ctx, nd) - if err != nil { - t.Fatal(err) - } - - links, err := api.Unixfs().Ls(ctx, path.FromCid(nd.Cid())) - if err != nil { - t.Fatal(err) - } - - if len(links) != 0 { - t.Fatalf("expected 0 links, got %d", len(links)) - } -} - -type closeTestF struct { - files.File - closed bool - - t *testing.T -} - -type closeTestD struct { - files.Directory - closed bool - - t *testing.T -} - -func (f *closeTestD) Close() error { - f.t.Helper() - if f.closed { - f.t.Fatal("already closed") - } - f.closed = true - return nil -} - -func (f *closeTestF) Close() error { - if f.closed { - f.t.Fatal("already closed") - } - f.closed = true - return nil -} - -func (tp *TestSuite) TestAddCloses(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - n4 := &closeTestF{files.NewBytesFile([]byte("foo")), false, t} - d3 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ - "sub": n4, - }), false, t} - n2 := &closeTestF{files.NewBytesFile([]byte("bar")), false, t} - n1 := &closeTestF{files.NewBytesFile([]byte("baz")), false, t} - d0 := &closeTestD{files.NewMapDirectory(map[string]files.Node{ - "a": d3, - "b": n1, - "c": n2, - }), false, t} - - _, err = api.Unixfs().Add(ctx, d0) - if err != nil { - t.Fatal(err) - } - - for i, n := range []*closeTestF{n1, n2, n4} { - if !n.closed { - t.Errorf("file %d not closed!", i) - } - } - - for i, n := range []*closeTestD{d0, d3} { - if !n.closed { - t.Errorf("dir %d not closed!", i) - } - } -} - -func (tp *TestSuite) TestGetSeek(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - dataSize := int64(100000) - tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) - - p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Unixfs().Get(ctx, p) - if err != nil { - t.Fatal(err) - } - - f := files.ToFile(r) - if f == nil { - t.Fatal("not a file") - } - - orig := make([]byte, dataSize) - if _, err := io.ReadFull(f, orig); err != nil { - t.Fatal(err) - } - f.Close() - - origR := bytes.NewReader(orig) - - r, err = api.Unixfs().Get(ctx, p) - if err != nil { - t.Fatal(err) - } - - f = files.ToFile(r) - if f == nil { - t.Fatal("not a file") - } - - test := func(offset int64, whence int, read int, expect int64, shouldEof bool) { - t.Run(fmt.Sprintf("seek%d+%d-r%d-%d", whence, offset, read, expect), func(t *testing.T) { - n, err := f.Seek(offset, whence) - if err != nil { - t.Fatal(err) - } - origN, err := origR.Seek(offset, whence) - if err != nil { - t.Fatal(err) - } - - if n != origN { - t.Fatalf("offsets didn't match, expected %d, got %d", origN, n) - } - - buf := make([]byte, read) - origBuf := make([]byte, read) - origRead, err := origR.Read(origBuf) - if err != nil { - t.Fatalf("orig: %s", err) - } - r, err := io.ReadFull(f, buf) - switch { - case shouldEof && err != nil && err != io.ErrUnexpectedEOF: - fallthrough - case !shouldEof && err != nil: - t.Fatalf("f: %s", err) - case shouldEof: - _, err := f.Read([]byte{0}) - if err != io.EOF { - t.Fatal("expected EOF") - } - _, err = origR.Read([]byte{0}) - if err != io.EOF { - t.Fatal("expected EOF (orig)") - } - } - - if int64(r) != expect { - t.Fatal("read wrong amount of data") - } - if r != origRead { - t.Fatal("read different amount of data than bytes.Reader") - } - if !bytes.Equal(buf, origBuf) { - fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) - fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) - t.Fatal("data didn't match") - } - }) - } - - test(3, io.SeekCurrent, 10, 10, false) - test(3, io.SeekCurrent, 10, 10, false) - test(500, io.SeekCurrent, 10, 10, false) - test(350, io.SeekStart, 100, 100, false) - test(-123, io.SeekCurrent, 100, 100, false) - test(0, io.SeekStart, int(dataSize), dataSize, false) - test(dataSize-50, io.SeekStart, 100, 50, true) - test(-5, io.SeekEnd, 100, 5, true) -} - -func (tp *TestSuite) TestGetReadAt(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - api, err := tp.makeAPI(t, ctx) - if err != nil { - t.Fatal(err) - } - - dataSize := int64(100000) - tf := files.NewReaderFile(io.LimitReader(rand.New(rand.NewSource(1403768328)), dataSize)) - - p, err := api.Unixfs().Add(ctx, tf, options.Unixfs.Chunker("size-100")) - if err != nil { - t.Fatal(err) - } - - r, err := api.Unixfs().Get(ctx, p) - if err != nil { - t.Fatal(err) - } - - f, ok := r.(interface { - files.File - io.ReaderAt - }) - if !ok { - t.Skip("ReaderAt not implemented") - } - - orig := make([]byte, dataSize) - if _, err := io.ReadFull(f, orig); err != nil { - t.Fatal(err) - } - f.Close() - - origR := bytes.NewReader(orig) - - if _, err := api.Unixfs().Get(ctx, p); err != nil { - t.Fatal(err) - } - - test := func(offset int64, read int, expect int64, shouldEof bool) { - t.Run(fmt.Sprintf("readat%d-r%d-%d", offset, read, expect), func(t *testing.T) { - origBuf := make([]byte, read) - origRead, err := origR.ReadAt(origBuf, offset) - if err != nil && err != io.EOF { - t.Fatalf("orig: %s", err) - } - buf := make([]byte, read) - r, err := f.ReadAt(buf, offset) - if shouldEof { - if err != io.EOF { - t.Fatal("expected EOF, got: ", err) - } - } else if err != nil { - t.Fatal("got: ", err) - } - - if int64(r) != expect { - t.Fatal("read wrong amount of data") - } - if r != origRead { - t.Fatal("read different amount of data than bytes.Reader") - } - if !bytes.Equal(buf, origBuf) { - fmt.Fprintf(os.Stderr, "original:\n%s\n", hex.Dump(origBuf)) - fmt.Fprintf(os.Stderr, "got:\n%s\n", hex.Dump(buf)) - t.Fatal("data didn't match") - } - }) - } - - test(3, 10, 10, false) - test(13, 10, 10, false) - test(513, 10, 10, false) - test(350, 100, 100, false) - test(0, int(dataSize), dataSize, false) - test(dataSize-50, 100, 50, true) -} diff --git a/coreiface/unixfs.go b/coreiface/unixfs.go deleted file mode 100644 index 35e108c02..000000000 --- a/coreiface/unixfs.go +++ /dev/null @@ -1,79 +0,0 @@ -package iface - -import ( - "context" - - "github.com/ipfs/boxo/coreiface/options" - "github.com/ipfs/boxo/files" - "github.com/ipfs/boxo/path" - "github.com/ipfs/go-cid" -) - -type AddEvent struct { - Name string - Path path.ImmutablePath `json:",omitempty"` - Bytes int64 `json:",omitempty"` - Size string `json:",omitempty"` -} - -// FileType is an enum of possible UnixFS file types. -type FileType int32 - -const ( - // TUnknown means the file type isn't known (e.g., it hasn't been - // resolved). - TUnknown FileType = iota - // TFile is a regular file. - TFile - // TDirectory is a directory. - TDirectory - // TSymlink is a symlink. - TSymlink -) - -func (t FileType) String() string { - switch t { - case TUnknown: - return "unknown" - case TFile: - return "file" - case TDirectory: - return "directory" - case TSymlink: - return "symlink" - default: - return "" - } -} - -// DirEntry is a directory entry returned by `Ls`. -type DirEntry struct { - Name string - Cid cid.Cid - - // Only filled when asked to resolve the directory entry. - Size uint64 // The size of the file in bytes (or the size of the symlink). - Type FileType // The type of the file. - Target string // The symlink target (if a symlink). - - Err error -} - -// UnixfsAPI is the basic interface to immutable files in IPFS -// NOTE: This API is heavily WIP, things are guaranteed to break frequently -type UnixfsAPI interface { - // Add imports the data from the reader into merkledag file - // - // TODO: a long useful comment on how to use this for many different scenarios - Add(context.Context, files.Node, ...options.UnixfsAddOption) (path.ImmutablePath, error) - - // Get returns a read-only handle to a file tree referenced by a path - // - // Note that some implementations of this API may apply the specified context - // to operations performed on the returned file - Get(context.Context, path.Path) (files.Node, error) - - // Ls returns the list of links in a directory. Links aren't guaranteed to be - // returned in order - Ls(context.Context, path.Path, ...options.UnixfsLsOption) (<-chan DirEntry, error) -} diff --git a/coreiface/util.go b/coreiface/util.go deleted file mode 100644 index 6d58bf40d..000000000 --- a/coreiface/util.go +++ /dev/null @@ -1,20 +0,0 @@ -package iface - -import ( - "context" - "io" -) - -type Reader interface { - ReadSeekCloser - Size() uint64 - CtxReadFull(context.Context, []byte) (int, error) -} - -// A ReadSeekCloser implements interfaces to read, copy, seek and close. -type ReadSeekCloser interface { - io.Reader - io.Seeker - io.Closer - io.WriterTo -} diff --git a/go.mod b/go.mod index 7d41cbda1..61d1c7bb8 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/ipfs/go-ipfs-blocksutil v0.0.1 github.com/ipfs/go-ipfs-delay v0.0.1 github.com/ipfs/go-ipfs-redirects-file v0.1.1 - github.com/ipfs/go-ipld-cbor v0.0.6 github.com/ipfs/go-ipld-format v0.5.0 github.com/ipfs/go-ipld-legacy v0.2.1 github.com/ipfs/go-log/v2 v2.5.1 @@ -106,6 +105,7 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/ipfs/go-ipfs-pq v0.0.3 // indirect github.com/ipfs/go-ipfs-util v0.0.2 // indirect + github.com/ipfs/go-ipld-cbor v0.0.6 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-unixfs v0.4.5 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect