github.com/storacha/go-ucanto@v0.7.2/core/delegation/delegation_test.go (about)

     1  package delegation
     2  
     3  import (
     4  	_ "embed"
     5  	"fmt"
     6  	"slices"
     7  	"testing"
     8  
     9  	"github.com/storacha/go-ucanto/core/dag/blockstore"
    10  	"github.com/storacha/go-ucanto/core/ipld"
    11  	"github.com/storacha/go-ucanto/core/ipld/block"
    12  	"github.com/storacha/go-ucanto/testing/fixtures"
    13  	"github.com/storacha/go-ucanto/testing/helpers"
    14  	"github.com/storacha/go-ucanto/ucan"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestExport(t *testing.T) {
    19  	prf, err := Delegate(
    20  		fixtures.Alice,
    21  		fixtures.Bob,
    22  		[]ucan.Capability[ucan.NoCaveats]{
    23  			ucan.NewCapability("test/proof", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
    24  		},
    25  	)
    26  	require.NoError(t, err)
    27  	dlg, err := Delegate(
    28  		fixtures.Bob,
    29  		fixtures.Mallory,
    30  		[]ucan.Capability[ucan.NoCaveats]{
    31  			ucan.NewCapability("test/proof", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
    32  		},
    33  		WithProof(
    34  			FromDelegation(prf),
    35  			// include an absent proof to prove things don't break - PUN INTENDED
    36  			FromLink(helpers.RandomCID()),
    37  		),
    38  	)
    39  	require.NoError(t, err)
    40  
    41  	bs, err := blockstore.NewBlockStore()
    42  	require.NoError(t, err)
    43  
    44  	var blks []ipld.Block
    45  	for b, err := range dlg.Blocks() {
    46  		require.NoError(t, err)
    47  		require.NoError(t, bs.Put(b))
    48  		blks = append(blks, b)
    49  	}
    50  	require.Len(t, blks, 2)
    51  	require.True(t, slices.ContainsFunc(blks, func(b ipld.Block) bool {
    52  		return b.Link().String() == prf.Link().String()
    53  	}))
    54  	require.True(t, slices.ContainsFunc(blks, func(b ipld.Block) bool {
    55  		return b.Link().String() == dlg.Link().String()
    56  	}))
    57  
    58  	// add an additional block to the blockstore that is not linked to by the
    59  	// delegation
    60  	otherblk := block.NewBlock(helpers.RandomCID(), helpers.RandomBytes(32))
    61  	err = bs.Put(otherblk)
    62  	require.NoError(t, err)
    63  
    64  	// reinstantiate delegation with our new blockstore
    65  	dlg, err = NewDelegationView(dlg.Link(), bs)
    66  	require.NoError(t, err)
    67  
    68  	var exblks []ipld.Block
    69  	// export the delegation from the blockstore
    70  	for b, err := range dlg.Export() {
    71  		require.NoError(t, err)
    72  		exblks = append(exblks, b)
    73  	}
    74  
    75  	// expect exblks to have the same blocks in the same order and it should not
    76  	// include otherblk
    77  	require.Len(t, exblks, len(blks))
    78  	for i, b := range blks {
    79  		require.Equal(t, b.Link().String(), exblks[i].Link().String())
    80  	}
    81  
    82  	// expect dlg.Blocks() to include otherblk though...
    83  	var blklnks []string
    84  	for b, err := range dlg.Blocks() {
    85  		require.NoError(t, err)
    86  		blklnks = append(blklnks, b.Link().String())
    87  	}
    88  	require.Contains(t, blklnks, otherblk.Link().String())
    89  }
    90  
    91  func TestExportOmitsProofs(t *testing.T) {
    92  	prf, err := Delegate(
    93  		fixtures.Alice,
    94  		fixtures.Bob,
    95  		[]ucan.Capability[ucan.NoCaveats]{
    96  			ucan.NewCapability("test/proof", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
    97  		},
    98  	)
    99  	require.NoError(t, err)
   100  	dlg, err := Delegate(
   101  		fixtures.Bob,
   102  		fixtures.Mallory,
   103  		[]ucan.Capability[ucan.NoCaveats]{
   104  			ucan.NewCapability("test/proof", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
   105  		},
   106  		WithProof(
   107  			FromDelegation(prf),
   108  			// include an absent proof to prove things don't break - PUN INTENDED
   109  			FromLink(helpers.RandomCID()),
   110  		),
   111  	)
   112  	require.NoError(t, err)
   113  
   114  	blks := map[string]struct{}{}
   115  	for b, err := range dlg.Export() {
   116  		require.NoError(t, err)
   117  		blks[b.Link().String()] = struct{}{}
   118  	}
   119  
   120  	exblks := map[string]struct{}{}
   121  	// export the delegation from the blockstore, excluding the proof
   122  	for b, err := range dlg.Export(WithOmitProof(prf.Link())) {
   123  		require.NoError(t, err)
   124  		exblks[b.Link().String()] = struct{}{}
   125  	}
   126  
   127  	require.Contains(t, blks, prf.Link().String())
   128  	require.NotContains(t, exblks, prf.Link().String())
   129  }
   130  
   131  func TestAttach(t *testing.T) {
   132  	dlg, err := Delegate(
   133  		fixtures.Alice,
   134  		fixtures.Bob,
   135  		[]ucan.Capability[ucan.NoCaveats]{
   136  			ucan.NewCapability("test/attach", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
   137  		},
   138  	)
   139  	require.NoError(t, err)
   140  
   141  	blk := block.NewBlock(helpers.RandomCID(), helpers.RandomBytes(32))
   142  	err = dlg.Attach(blk)
   143  	require.NoError(t, err)
   144  
   145  	var blklnks []string
   146  	for b, err := range dlg.Blocks() {
   147  		require.NoError(t, err)
   148  		blklnks = append(blklnks, b.Link().String())
   149  	}
   150  	require.Contains(t, blklnks, blk.Link().String())
   151  }
   152  
   153  func TestFormatParse(t *testing.T) {
   154  	dlg, err := Delegate(
   155  		fixtures.Alice,
   156  		fixtures.Bob,
   157  		[]ucan.Capability[ucan.NoCaveats]{
   158  			ucan.NewCapability("test/proof", fixtures.Alice.DID().String(), ucan.NoCaveats{}),
   159  		},
   160  	)
   161  	require.NoError(t, err)
   162  
   163  	formatted, err := Format(dlg)
   164  	require.NoError(t, err)
   165  
   166  	fmt.Println(formatted)
   167  
   168  	parsed, err := Parse(formatted)
   169  	require.NoError(t, err)
   170  
   171  	require.Equal(t, dlg.Link(), parsed.Link())
   172  }
   173  
   174  //go:embed delegationnonb.car
   175  var delegationnonb []byte
   176  
   177  // delegationnonb.car is an archived delegation with a capability with no `nb`.
   178  // This is (currently) difficult to accomplish in Go, but perfectly common in
   179  // JS, leading to delegations from a server which a Go client chokes on (hence
   180  // this test). The delegation was generated with JS like the following:
   181  
   182  // const client = await getClient()
   183  //
   184  // const delegation = await delegate({
   185  //   issuer: client.agent.issuer,
   186  //   audience: DID.parse('did:example:alice'),
   187  //   capabilities: [
   188  //     {
   189  //       with: 'did:key:123456789',
   190  //       can: 'do/something',
   191  //     },
   192  //   ],
   193  // })
   194  //
   195  // const res = await delegation.archive()
   196  // fs.writeFileSync('delegationnonb.car', res.ok)
   197  
   198  func TestParseNoNb(t *testing.T) {
   199  	// An archived delegation with a capability with no `nb`
   200  	dlg, err := Extract(delegationnonb)
   201  	require.NoError(t, err)
   202  	require.Equal(t, "did:key:z6MkpveRpPySqSVXyhAmWbyQLdY9w5noKr1Ff2MX8P9htje9", dlg.Issuer().DID().String())
   203  	require.Equal(t, "did:example:alice", dlg.Audience().DID().String())
   204  	require.Len(t, dlg.Capabilities(), 1)
   205  	require.Equal(t, "do/something", dlg.Capabilities()[0].Can())
   206  	require.Equal(t, "did:key:123456789", dlg.Capabilities()[0].With())
   207  	require.Equal(t, nil, dlg.Capabilities()[0].Nb())
   208  }