oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/internal/graph/memory_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package graph
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"io"
    23  	"reflect"
    24  	"testing"
    25  
    26  	"github.com/opencontainers/go-digest"
    27  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    28  	"oras.land/oras-go/v2/internal/cas"
    29  	"oras.land/oras-go/v2/internal/descriptor"
    30  )
    31  
    32  // +------------------------------+
    33  // |                              |
    34  // |  +-----------+               |
    35  // |  |A(manifest)|               |
    36  // |  +-----+-----+               |
    37  // |        |                     |
    38  // |        +------------+        |
    39  // |        |            |        |
    40  // |        v            v        |
    41  // |  +-----+-----+  +---+----+   |
    42  // |  |B(manifest)|  |C(layer)|   |
    43  // |  +-----+-----+  +--------+   |
    44  // |        |                     |
    45  // |        v                     |
    46  // |    +---+----+                |
    47  // |    |D(layer)|                |
    48  // |    +--------+                |
    49  // |                              |
    50  // |------------------------------+
    51  func TestMemory_IndexAndRemove(t *testing.T) {
    52  	testFetcher := cas.NewMemory()
    53  	testMemory := NewMemory()
    54  	ctx := context.Background()
    55  
    56  	// generate test content
    57  	var blobs [][]byte
    58  	var descs []ocispec.Descriptor
    59  	appendBlob := func(mediaType string, blob []byte) ocispec.Descriptor {
    60  		blobs = append(blobs, blob)
    61  		descs = append(descs, ocispec.Descriptor{
    62  			MediaType: mediaType,
    63  			Digest:    digest.FromBytes(blob),
    64  			Size:      int64(len(blob)),
    65  		})
    66  		return descs[len(descs)-1]
    67  	}
    68  	generateManifest := func(layers ...ocispec.Descriptor) ocispec.Descriptor {
    69  		manifest := ocispec.Manifest{
    70  			Config: ocispec.Descriptor{MediaType: "test config"},
    71  			Layers: layers,
    72  		}
    73  		manifestJSON, err := json.Marshal(manifest)
    74  		if err != nil {
    75  			t.Fatal(err)
    76  		}
    77  		return appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
    78  	}
    79  	descC := appendBlob("layer node C", []byte("Node C is a layer")) // blobs[0], layer "C"
    80  	descD := appendBlob("layer node D", []byte("Node D is a layer")) // blobs[1], layer "D"
    81  	descB := generateManifest(descs[0:2]...)                         // blobs[2], manifest "B"
    82  	descA := generateManifest(descs[1:3]...)                         // blobs[3], manifest "A"
    83  
    84  	// prepare the content in the fetcher, so that it can be used to test Index
    85  	testContents := []ocispec.Descriptor{descC, descD, descB, descA}
    86  	for i := 0; i < len(blobs); i++ {
    87  		testFetcher.Push(ctx, testContents[i], bytes.NewReader(blobs[i]))
    88  	}
    89  
    90  	// make sure that testFetcher works
    91  	rc, err := testFetcher.Fetch(ctx, descA)
    92  	if err != nil {
    93  		t.Errorf("testFetcher.Fetch() error = %v", err)
    94  	}
    95  	got, err := io.ReadAll(rc)
    96  	if err != nil {
    97  		t.Errorf("testFetcher.Fetch().Read() error = %v", err)
    98  	}
    99  	err = rc.Close()
   100  	if err != nil {
   101  		t.Errorf("testFetcher.Fetch().Close() error = %v", err)
   102  	}
   103  	if !bytes.Equal(got, blobs[3]) {
   104  		t.Errorf("testFetcher.Fetch() = %v, want %v", got, blobs[4])
   105  	}
   106  
   107  	nodeKeyA := descriptor.FromOCI(descA)
   108  	nodeKeyB := descriptor.FromOCI(descB)
   109  	nodeKeyC := descriptor.FromOCI(descC)
   110  	nodeKeyD := descriptor.FromOCI(descD)
   111  
   112  	// index and check the information of node D
   113  	testMemory.Index(ctx, testFetcher, descD)
   114  	// 1. verify its existence in testMemory.nodes
   115  	if _, exists := testMemory.nodes[nodeKeyD]; !exists {
   116  		t.Errorf("nodes entry of %s should exist", "D")
   117  	}
   118  	// 2. verify that the entry of D exists in testMemory.successors and it's empty
   119  	successorsD, exists := testMemory.successors[nodeKeyD]
   120  	if !exists {
   121  		t.Errorf("successor entry of %s should exist", "D")
   122  	}
   123  	if successorsD == nil {
   124  		t.Errorf("successors of %s should be an empty set, not nil", "D")
   125  	}
   126  	if len(successorsD) != 0 {
   127  		t.Errorf("successors of %s should be empty", "D")
   128  	}
   129  	// 3. there should be no entry of D in testMemory.predecessors yet
   130  	_, exists = testMemory.predecessors[nodeKeyD]
   131  	if exists {
   132  		t.Errorf("predecessor entry of %s should not exist yet", "D")
   133  	}
   134  
   135  	// index and check the information of node C
   136  	testMemory.Index(ctx, testFetcher, descC)
   137  	// 1. verify its existence in memory.nodes
   138  	if _, exists := testMemory.nodes[nodeKeyC]; !exists {
   139  		t.Errorf("nodes entry of %s should exist", "C")
   140  	}
   141  	// 2. verify that the entry of C exists in testMemory.successors and it's empty
   142  	successorsC, exists := testMemory.successors[nodeKeyC]
   143  	if !exists {
   144  		t.Errorf("successor entry of %s should exist", "C")
   145  	}
   146  	if successorsC == nil {
   147  		t.Errorf("successors of %s should be an empty set, not nil", "C")
   148  	}
   149  	if len(successorsC) != 0 {
   150  		t.Errorf("successors of %s should be empty", "C")
   151  	}
   152  	// 3. there should be no entry of C in testMemory.predecessors yet
   153  	_, exists = testMemory.predecessors[nodeKeyC]
   154  	if exists {
   155  		t.Errorf("predecessor entry of %s should not exist yet", "C")
   156  	}
   157  
   158  	// index and check the information of node A
   159  	testMemory.Index(ctx, testFetcher, descA)
   160  	// 1. verify its existence in testMemory.nodes
   161  	if _, exists := testMemory.nodes[nodeKeyA]; !exists {
   162  		t.Errorf("nodes entry of %s should exist", "A")
   163  	}
   164  	// 2. verify that the entry of A exists in testMemory.successors and it contains
   165  	// node B and node D
   166  	successorsA, exists := testMemory.successors[nodeKeyA]
   167  	if !exists {
   168  		t.Errorf("successor entry of %s should exist", "A")
   169  	}
   170  	if successorsA == nil {
   171  		t.Errorf("successors of %s should be a set, not nil", "A")
   172  	}
   173  	if !successorsA.Contains(nodeKeyB) {
   174  		t.Errorf("successors of %s should contain %s", "A", "B")
   175  	}
   176  	if !successorsA.Contains(nodeKeyD) {
   177  		t.Errorf("successors of %s should contain %s", "A", "D")
   178  	}
   179  	// 3. verify that node A exists in the predecessors lists of its successors.
   180  	// there should be an entry of D in testMemory.predecessors by now and it
   181  	// should contain A but not B
   182  	predecessorsD, exists := testMemory.predecessors[nodeKeyD]
   183  	if !exists {
   184  		t.Errorf("predecessor entry of %s should exist by now", "D")
   185  	}
   186  	if !predecessorsD.Contains(nodeKeyA) {
   187  		t.Errorf("predecessors of %s should contain %s", "D", "A")
   188  	}
   189  	if predecessorsD.Contains(nodeKeyB) {
   190  		t.Errorf("predecessors of %s should not contain %s yet", "D", "B")
   191  	}
   192  	// there should be an entry of B in testMemory.predecessors now
   193  	// and it should contain A
   194  	predecessorsB, exists := testMemory.predecessors[nodeKeyB]
   195  	if !exists {
   196  		t.Errorf("predecessor entry of %s should exist by now", "B")
   197  	}
   198  	if !predecessorsB.Contains(nodeKeyA) {
   199  		t.Errorf("predecessors of %s should contain %s", "B", "A")
   200  	}
   201  	// 4. there should be no entry of A in testMemory.predecessors
   202  	_, exists = testMemory.predecessors[nodeKeyA]
   203  	if exists {
   204  		t.Errorf("predecessor entry of %s should not exist", "A")
   205  	}
   206  
   207  	// index and check the information of node B
   208  	testMemory.Index(ctx, testFetcher, descB)
   209  	// 1. verify its existence in testMemory.nodes
   210  	if _, exists := testMemory.nodes[nodeKeyB]; !exists {
   211  		t.Errorf("nodes entry of %s should exist", "B")
   212  	}
   213  	// 2. verify that the entry of B exists in testMemory.successors and it contains
   214  	// node C and node D
   215  	successorsB, exists := testMemory.successors[nodeKeyB]
   216  	if !exists {
   217  		t.Errorf("successor entry of %s should exist", "B")
   218  	}
   219  	if successorsB == nil {
   220  		t.Errorf("successors of %s should be a set, not nil", "B")
   221  	}
   222  	if !successorsB.Contains(nodeKeyC) {
   223  		t.Errorf("successors of %s should contain %s", "B", "C")
   224  	}
   225  	if !successorsB.Contains(nodeKeyD) {
   226  		t.Errorf("successors of %s should contain %s", "B", "D")
   227  	}
   228  	// 3. verify that node B exists in the predecessors lists of its successors.
   229  	// there should be an entry of C in testMemory.predecessors by now
   230  	// and it should contain B
   231  	predecessorsC, exists := testMemory.predecessors[nodeKeyC]
   232  	if !exists {
   233  		t.Errorf("predecessor entry of %s should exist by now", "C")
   234  	}
   235  	if !predecessorsC.Contains(nodeKeyB) {
   236  		t.Errorf("predecessors of %s should contain %s", "C", "B")
   237  	}
   238  	// predecessors of D should have been updated now to have node A and B
   239  	if !predecessorsD.Contains(nodeKeyB) {
   240  		t.Errorf("predecessors of %s should contain %s", "D", "B")
   241  	}
   242  	if !predecessorsD.Contains(nodeKeyA) {
   243  		t.Errorf("predecessors of %s should contain %s", "D", "A")
   244  	}
   245  
   246  	// remove node B and check the stored information
   247  	testMemory.Remove(descB)
   248  	// 1. verify that node B no longer exists in testMemory.nodes
   249  	if _, exists := testMemory.nodes[nodeKeyB]; exists {
   250  		t.Errorf("nodes entry of %s should no longer exist", "B")
   251  	}
   252  	// 2. verify B' predecessors info: B's entry in testMemory.predecessors should
   253  	// still exist, since its predecessor A still exists
   254  	predecessorsB, exists = testMemory.predecessors[nodeKeyB]
   255  	if !exists {
   256  		t.Errorf("testDeletableMemory.predecessors should still contain the entry of %s", "B")
   257  	}
   258  	if !predecessorsB.Contains(nodeKeyA) {
   259  		t.Errorf("predecessors of %s should still contain %s", "B", "A")
   260  	}
   261  	// 3. verify B' successors info: B's entry in testMemory.successors should no
   262  	// longer exist
   263  	if _, exists := testMemory.successors[nodeKeyB]; exists {
   264  		t.Errorf("testDeletableMemory.successors should not contain the entry of %s", "B")
   265  	}
   266  	// 4. verify B' predecessors' successors info: B should still exist in A's
   267  	// successors
   268  	if !successorsA.Contains(nodeKeyB) {
   269  		t.Errorf("successors of %s should still contain %s", "A", "B")
   270  	}
   271  	// 5. verify B' successors' predecessors info: C's entry in testMemory.predecessors
   272  	// should no longer exist, since C's only predecessor B is already deleted
   273  	if _, exists = testMemory.predecessors[nodeKeyC]; exists {
   274  		t.Errorf("predecessor entry of %s should no longer exist by now, since all its predecessors have been deleted", "C")
   275  	}
   276  	// B should no longer exist in D's predecessors
   277  	if predecessorsD.Contains(nodeKeyB) {
   278  		t.Errorf("predecessors of %s should not contain %s", "D", "B")
   279  	}
   280  	// but A still exists in D's predecessors
   281  	if !predecessorsD.Contains(nodeKeyA) {
   282  		t.Errorf("predecessors of %s should still contain %s", "D", "A")
   283  	}
   284  
   285  	// remove node A and check the stored information
   286  	testMemory.Remove(descA)
   287  	// 1. verify that node A no longer exists in testMemory.nodes
   288  	if _, exists := testMemory.nodes[nodeKeyA]; exists {
   289  		t.Errorf("nodes entry of %s should no longer exist", "A")
   290  	}
   291  	// 2. verify A' successors info: A's entry in testMemory.successors should no
   292  	// longer exist
   293  	if _, exists := testMemory.successors[nodeKeyA]; exists {
   294  		t.Errorf("testDeletableMemory.successors should not contain the entry of %s", "A")
   295  	}
   296  	// 3. verify A' successors' predecessors info: D's entry in testMemory.predecessors
   297  	// should no longer exist, since all predecessors of D are already deleted
   298  	if _, exists = testMemory.predecessors[nodeKeyD]; exists {
   299  		t.Errorf("predecessor entry of %s should no longer exist by now, since all its predecessors have been deleted", "D")
   300  	}
   301  	// B's entry in testMemory.predecessors should no longer exist, since B's only
   302  	// predecessor A is already deleted
   303  	if _, exists = testMemory.predecessors[nodeKeyB]; exists {
   304  		t.Errorf("predecessor entry of %s should no longer exist by now, since all its predecessors have been deleted", "B")
   305  	}
   306  }
   307  
   308  // +-----------------------------------------------+
   309  // |                                               |
   310  // |                   +--------+                  |
   311  // |                   |A(index)|                  |
   312  // |                   +---+----+                  |
   313  // |                       |                       |
   314  // |       -+--------------+--------------+-       |
   315  // |        |              |              |        |
   316  // |  +-----v-----+  +-----v-----+  +-----v-----+  |
   317  // |  |B(manifest)|  |C(manifest)|  |D(manifest)|  |
   318  // |  +--------+--+  ++---------++  +--+--------+  |
   319  // |           |      |         |      |           |
   320  // |           |      |         |      |           |
   321  // |           v      v         v      v           |
   322  // |          ++------++       ++------++          |
   323  // |          |E(layer)|       |F(layer)|          |
   324  // |          +--------+       +--------+          |
   325  // |                                               |
   326  // +-----------------------------------------------+
   327  func TestMemory_IndexAllAndPredecessors(t *testing.T) {
   328  	testFetcher := cas.NewMemory()
   329  	testMemory := NewMemory()
   330  	ctx := context.Background()
   331  
   332  	// generate test content
   333  	var blobs [][]byte
   334  	var descriptors []ocispec.Descriptor
   335  	appendBlob := func(mediaType string, blob []byte) ocispec.Descriptor {
   336  		blobs = append(blobs, blob)
   337  		descriptors = append(descriptors, ocispec.Descriptor{
   338  			MediaType: mediaType,
   339  			Digest:    digest.FromBytes(blob),
   340  			Size:      int64(len(blob)),
   341  		})
   342  		return descriptors[len(descriptors)-1]
   343  	}
   344  	generateManifest := func(layers ...ocispec.Descriptor) ocispec.Descriptor {
   345  		manifest := ocispec.Manifest{
   346  			Config: ocispec.Descriptor{MediaType: "test config"},
   347  			Layers: layers,
   348  		}
   349  		manifestJSON, err := json.Marshal(manifest)
   350  		if err != nil {
   351  			t.Fatal(err)
   352  		}
   353  		return appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   354  	}
   355  	generateIndex := func(manifests ...ocispec.Descriptor) ocispec.Descriptor {
   356  		index := ocispec.Index{
   357  			Manifests: manifests,
   358  		}
   359  		indexJSON, err := json.Marshal(index)
   360  		if err != nil {
   361  			t.Fatal(err)
   362  		}
   363  		return appendBlob(ocispec.MediaTypeImageIndex, indexJSON)
   364  	}
   365  	descE := appendBlob("layer node E", []byte("Node E is a layer")) // blobs[0], layer "E"
   366  	descF := appendBlob("layer node F", []byte("Node F is a layer")) // blobs[1], layer "F"
   367  	descB := generateManifest(descriptors[0:1]...)                   // blobs[2], manifest "B"
   368  	descC := generateManifest(descriptors[0:2]...)                   // blobs[3], manifest "C"
   369  	descD := generateManifest(descriptors[1:2]...)                   // blobs[4], manifest "D"
   370  	descA := generateIndex(descriptors[2:5]...)                      // blobs[5], index "A"
   371  
   372  	// prepare the content in the fetcher, so that it can be used to test IndexAll
   373  	testContents := []ocispec.Descriptor{descE, descF, descB, descC, descD, descA}
   374  	for i := 0; i < len(blobs); i++ {
   375  		testFetcher.Push(ctx, testContents[i], bytes.NewReader(blobs[i]))
   376  	}
   377  
   378  	// make sure that testFetcher works
   379  	rc, err := testFetcher.Fetch(ctx, descA)
   380  	if err != nil {
   381  		t.Errorf("testFetcher.Fetch() error = %v", err)
   382  	}
   383  	got, err := io.ReadAll(rc)
   384  	if err != nil {
   385  		t.Errorf("testFetcher.Fetch().Read() error = %v", err)
   386  	}
   387  	err = rc.Close()
   388  	if err != nil {
   389  		t.Errorf("testFetcher.Fetch().Close() error = %v", err)
   390  	}
   391  	if !bytes.Equal(got, blobs[5]) {
   392  		t.Errorf("testFetcher.Fetch() = %v, want %v", got, blobs[4])
   393  	}
   394  
   395  	nodeKeyA := descriptor.FromOCI(descA)
   396  	nodeKeyB := descriptor.FromOCI(descB)
   397  	nodeKeyC := descriptor.FromOCI(descC)
   398  	nodeKeyD := descriptor.FromOCI(descD)
   399  	nodeKeyE := descriptor.FromOCI(descE)
   400  	nodeKeyF := descriptor.FromOCI(descF)
   401  
   402  	// index node A into testMemory using IndexAll
   403  	testMemory.IndexAll(ctx, testFetcher, descA)
   404  
   405  	// check the information of node A
   406  	// 1. verify that node A exists in testMemory.nodes
   407  	if _, exists := testMemory.nodes[nodeKeyA]; !exists {
   408  		t.Errorf("nodes entry of %s should exist", "A")
   409  	}
   410  	// 2. verify that there is no entry of A in predecessors
   411  	if _, exists := testMemory.predecessors[nodeKeyA]; exists {
   412  		t.Errorf("there should be no entry of %s in predecessors", "A")
   413  	}
   414  	// 3. verify that A has successors B, C, D
   415  	successorsA, exists := testMemory.successors[nodeKeyA]
   416  	if !exists {
   417  		t.Errorf("there should be an entry of %s in successors", "A")
   418  	}
   419  	if !successorsA.Contains(nodeKeyB) {
   420  		t.Errorf("successors of %s should contain %s", "A", "B")
   421  	}
   422  	if !successorsA.Contains(nodeKeyC) {
   423  		t.Errorf("successors of %s should contain %s", "A", "C")
   424  	}
   425  	if !successorsA.Contains(nodeKeyD) {
   426  		t.Errorf("successors of %s should contain %s", "A", "D")
   427  	}
   428  
   429  	// check the information of node B
   430  	// 1. verify that node B exists in testMemory.nodes
   431  	if _, exists := testMemory.nodes[nodeKeyB]; !exists {
   432  		t.Errorf("nodes entry of %s should exist", "B")
   433  	}
   434  	// 2. verify that B has node A in its predecessors
   435  	predecessorsB := testMemory.predecessors[nodeKeyB]
   436  	if !predecessorsB.Contains(nodeKeyA) {
   437  		t.Errorf("predecessors of %s should contain %s", "B", "A")
   438  	}
   439  	// 3. verify that B has node E in its successors
   440  	successorsB := testMemory.successors[nodeKeyB]
   441  	if !successorsB.Contains(nodeKeyE) {
   442  		t.Errorf("successors of %s should contain %s", "B", "E")
   443  	}
   444  
   445  	// check the information of node C
   446  	// 1. verify that node C exists in testMemory.nodes
   447  	if _, exists := testMemory.nodes[nodeKeyC]; !exists {
   448  		t.Errorf("nodes entry of %s should exist", "C")
   449  	}
   450  	// 2. verify that C has node A in its predecessors
   451  	predecessorsC := testMemory.predecessors[nodeKeyC]
   452  	if !predecessorsC.Contains(nodeKeyA) {
   453  		t.Errorf("predecessors of %s should contain %s", "C", "A")
   454  	}
   455  	// 3. verify that C has node E and F in its successors
   456  	successorsC := testMemory.successors[nodeKeyC]
   457  	if !successorsC.Contains(nodeKeyE) {
   458  		t.Errorf("successors of %s should contain %s", "C", "E")
   459  	}
   460  	if !successorsC.Contains(nodeKeyF) {
   461  		t.Errorf("successors of %s should contain %s", "C", "F")
   462  	}
   463  
   464  	// check the information of node D
   465  	// 1. verify that node D exists in testMemory.nodes
   466  	if _, exists := testMemory.nodes[nodeKeyD]; !exists {
   467  		t.Errorf("nodes entry of %s should exist", "D")
   468  	}
   469  	// 2. verify that D has node A in its predecessors
   470  	predecessorsD := testMemory.predecessors[nodeKeyD]
   471  	if !predecessorsD.Contains(nodeKeyA) {
   472  		t.Errorf("predecessors of %s should contain %s", "D", "A")
   473  	}
   474  	// 3. verify that D has node F in its successors
   475  	successorsD := testMemory.successors[nodeKeyD]
   476  	if !successorsD.Contains(nodeKeyF) {
   477  		t.Errorf("successors of %s should contain %s", "D", "F")
   478  	}
   479  
   480  	// check the information of node E
   481  	// 1. verify that node E exists in testMemory.nodes
   482  	if _, exists := testMemory.nodes[nodeKeyE]; !exists {
   483  		t.Errorf("nodes entry of %s should exist", "E")
   484  	}
   485  	// 2. verify that E has node B and C in its predecessors
   486  	predecessorsE := testMemory.predecessors[nodeKeyE]
   487  	if !predecessorsE.Contains(nodeKeyB) {
   488  		t.Errorf("predecessors of %s should contain %s", "E", "B")
   489  	}
   490  	if !predecessorsE.Contains(nodeKeyC) {
   491  		t.Errorf("predecessors of %s should contain %s", "E", "C")
   492  	}
   493  	// 3. verify that E has an entry in successors and it's empty
   494  	successorsE, exists := testMemory.successors[nodeKeyE]
   495  	if !exists {
   496  		t.Errorf("entry %s should exist in testMemory.successors", "E")
   497  	}
   498  	if successorsE == nil {
   499  		t.Errorf("successors of %s should be an empty set, not nil", "E")
   500  	}
   501  	if len(successorsE) != 0 {
   502  		t.Errorf("successors of %s should be empty", "E")
   503  	}
   504  
   505  	// check the information of node F
   506  	// 1. verify that node F exists in testMemory.nodes
   507  	if _, exists := testMemory.nodes[nodeKeyF]; !exists {
   508  		t.Errorf("nodes entry of %s should exist", "F")
   509  	}
   510  	// 2. verify that F has node C and D in its predecessors
   511  	predecessorsF := testMemory.predecessors[nodeKeyF]
   512  	if !predecessorsF.Contains(nodeKeyC) {
   513  		t.Errorf("predecessors of %s should contain %s", "F", "C")
   514  	}
   515  	if !predecessorsF.Contains(nodeKeyD) {
   516  		t.Errorf("predecessors of %s should contain %s", "F", "D")
   517  	}
   518  	// 3. verify that F has an entry in successors and it's empty
   519  	successorsF, exists := testMemory.successors[nodeKeyF]
   520  	if !exists {
   521  		t.Errorf("entry %s should exist in testMemory.successors", "F")
   522  	}
   523  	if successorsF == nil {
   524  		t.Errorf("successors of %s should be an empty set, not nil", "F")
   525  	}
   526  	if len(successorsF) != 0 {
   527  		t.Errorf("successors of %s should be empty", "F")
   528  	}
   529  
   530  	// check that the Predecessors of node C is node A
   531  	predsC, err := testMemory.Predecessors(ctx, descC)
   532  	if err != nil {
   533  		t.Errorf("testFetcher.Predecessors() error = %v", err)
   534  	}
   535  	expectedLength := 1
   536  	if len(predsC) != expectedLength {
   537  		t.Errorf("%s should have length %d", "predsC", expectedLength)
   538  	}
   539  	if !reflect.DeepEqual(predsC[0], descA) {
   540  		t.Errorf("incorrect predecessor result")
   541  	}
   542  
   543  	// check that the Predecessors of node F are node C and node D
   544  	predsF, err := testMemory.Predecessors(ctx, descF)
   545  	if err != nil {
   546  		t.Errorf("testFetcher.Predecessors() error = %v", err)
   547  	}
   548  	expectedLength = 2
   549  	if len(predsF) != expectedLength {
   550  		t.Errorf("%s should have length %d", "predsF", expectedLength)
   551  	}
   552  	for _, pred := range predsF {
   553  		if !reflect.DeepEqual(pred, descC) && !reflect.DeepEqual(pred, descD) {
   554  			t.Errorf("incorrect predecessor results")
   555  		}
   556  	}
   557  
   558  	// remove node C and check the stored information
   559  	testMemory.Remove(descC)
   560  	if predecessorsE.Contains(nodeKeyC) {
   561  		t.Errorf("predecessors of %s should not contain %s", "E", "C")
   562  	}
   563  	if predecessorsF.Contains(nodeKeyC) {
   564  		t.Errorf("predecessors of %s should not contain %s", "F", "C")
   565  	}
   566  	if !successorsA.Contains(nodeKeyC) {
   567  		t.Errorf("successors of %s should still contain %s", "A", "C")
   568  	}
   569  	if _, exists := testMemory.successors[nodeKeyC]; exists {
   570  		t.Errorf("testMemory.successors should not contain the entry of %s", "C")
   571  	}
   572  	if _, exists := testMemory.predecessors[nodeKeyC]; !exists {
   573  		t.Errorf("entry %s in predecessors should still exists since it still has at least one predecessor node present", "C")
   574  	}
   575  
   576  	// remove node A and check the stored information
   577  	testMemory.Remove(descA)
   578  	if _, exists := testMemory.predecessors[nodeKeyB]; exists {
   579  		t.Errorf("entry %s in predecessors should no longer exists", "B")
   580  	}
   581  	if _, exists := testMemory.predecessors[nodeKeyC]; exists {
   582  		t.Errorf("entry %s in predecessors should no longer exists", "C")
   583  	}
   584  	if _, exists := testMemory.predecessors[nodeKeyD]; exists {
   585  		t.Errorf("entry %s in predecessors should no longer exists", "D")
   586  	}
   587  	if _, exists := testMemory.successors[nodeKeyA]; exists {
   588  		t.Errorf("testDeletableMemory.successors should not contain the entry of %s", "A")
   589  	}
   590  
   591  	// check that the Predecessors of node D is empty
   592  	predsD, err := testMemory.Predecessors(ctx, descD)
   593  	if err != nil {
   594  		t.Errorf("testFetcher.Predecessors() error = %v", err)
   595  	}
   596  	if predsD != nil {
   597  		t.Errorf("%s should be nil", "predsD")
   598  	}
   599  
   600  	// check that the Predecessors of node E is node B
   601  	predsE, err := testMemory.Predecessors(ctx, descE)
   602  	if err != nil {
   603  		t.Errorf("testFetcher.Predecessors() error = %v", err)
   604  	}
   605  	expectedLength = 1
   606  	if len(predsE) != expectedLength {
   607  		t.Errorf("%s should have length %d", "predsE", expectedLength)
   608  	}
   609  	if !reflect.DeepEqual(predsE[0], descB) {
   610  		t.Errorf("incorrect predecessor result")
   611  	}
   612  }
   613  
   614  func TestMemory_DigestSet(t *testing.T) {
   615  	testFetcher := cas.NewMemory()
   616  	testMemory := NewMemory()
   617  	ctx := context.Background()
   618  
   619  	// generate test content
   620  	var blobs [][]byte
   621  	var descriptors []ocispec.Descriptor
   622  	appendBlob := func(mediaType string, blob []byte) ocispec.Descriptor {
   623  		blobs = append(blobs, blob)
   624  		descriptors = append(descriptors, ocispec.Descriptor{
   625  			MediaType: mediaType,
   626  			Digest:    digest.FromBytes(blob),
   627  			Size:      int64(len(blob)),
   628  		})
   629  		return descriptors[len(descriptors)-1]
   630  	}
   631  	generateManifest := func(layers ...ocispec.Descriptor) ocispec.Descriptor {
   632  		manifest := ocispec.Manifest{
   633  			Config: ocispec.Descriptor{MediaType: "test config"},
   634  			Layers: layers,
   635  		}
   636  		manifestJSON, err := json.Marshal(manifest)
   637  		if err != nil {
   638  			t.Fatal(err)
   639  		}
   640  		return appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   641  	}
   642  	generateIndex := func(manifests ...ocispec.Descriptor) ocispec.Descriptor {
   643  		index := ocispec.Index{
   644  			Manifests: manifests,
   645  		}
   646  		indexJSON, err := json.Marshal(index)
   647  		if err != nil {
   648  			t.Fatal(err)
   649  		}
   650  		return appendBlob(ocispec.MediaTypeImageIndex, indexJSON)
   651  	}
   652  	descE := appendBlob("layer node E", []byte("Node E is a layer")) // blobs[0], layer "E"
   653  	descF := appendBlob("layer node F", []byte("Node F is a layer")) // blobs[1], layer "F"
   654  	descB := generateManifest(descriptors[0:1]...)                   // blobs[2], manifest "B"
   655  	descC := generateManifest(descriptors[0:2]...)                   // blobs[3], manifest "C"
   656  	descD := generateManifest(descriptors[1:2]...)                   // blobs[4], manifest "D"
   657  	descA := generateIndex(descriptors[2:5]...)                      // blobs[5], index "A"
   658  
   659  	// prepare the content in the fetcher, so that it can be used to test IndexAll
   660  	testContents := []ocispec.Descriptor{descE, descF, descB, descC, descD, descA}
   661  	for i := 0; i < len(blobs); i++ {
   662  		testFetcher.Push(ctx, testContents[i], bytes.NewReader(blobs[i]))
   663  	}
   664  
   665  	// make sure that testFetcher works
   666  	rc, err := testFetcher.Fetch(ctx, descA)
   667  	if err != nil {
   668  		t.Errorf("testFetcher.Fetch() error = %v", err)
   669  	}
   670  	got, err := io.ReadAll(rc)
   671  	if err != nil {
   672  		t.Errorf("testFetcher.Fetch().Read() error = %v", err)
   673  	}
   674  	err = rc.Close()
   675  	if err != nil {
   676  		t.Errorf("testFetcher.Fetch().Close() error = %v", err)
   677  	}
   678  	if !bytes.Equal(got, blobs[5]) {
   679  		t.Errorf("testFetcher.Fetch() = %v, want %v", got, blobs[4])
   680  	}
   681  
   682  	// index node A into testMemory using IndexAll
   683  	testMemory.IndexAll(ctx, testFetcher, descA)
   684  	digestSet := testMemory.DigestSet()
   685  	for i := 0; i < len(blobs); i++ {
   686  		if exists := digestSet.Contains(descriptors[i].Digest); exists != true {
   687  			t.Errorf("digest of blob[%d] should exist in digestSet", i)
   688  		}
   689  	}
   690  }
   691  
   692  func TestMemory_Exists(t *testing.T) {
   693  	testFetcher := cas.NewMemory()
   694  	testMemory := NewMemory()
   695  	ctx := context.Background()
   696  
   697  	// generate test content
   698  	var blobs [][]byte
   699  	var descriptors []ocispec.Descriptor
   700  	appendBlob := func(mediaType string, blob []byte) ocispec.Descriptor {
   701  		blobs = append(blobs, blob)
   702  		descriptors = append(descriptors, ocispec.Descriptor{
   703  			MediaType: mediaType,
   704  			Digest:    digest.FromBytes(blob),
   705  			Size:      int64(len(blob)),
   706  		})
   707  		return descriptors[len(descriptors)-1]
   708  	}
   709  	generateManifest := func(layers ...ocispec.Descriptor) ocispec.Descriptor {
   710  		manifest := ocispec.Manifest{
   711  			Config: ocispec.Descriptor{MediaType: "test config"},
   712  			Layers: layers,
   713  		}
   714  		manifestJSON, err := json.Marshal(manifest)
   715  		if err != nil {
   716  			t.Fatal(err)
   717  		}
   718  		return appendBlob(ocispec.MediaTypeImageManifest, manifestJSON)
   719  	}
   720  	generateIndex := func(manifests ...ocispec.Descriptor) ocispec.Descriptor {
   721  		index := ocispec.Index{
   722  			Manifests: manifests,
   723  		}
   724  		indexJSON, err := json.Marshal(index)
   725  		if err != nil {
   726  			t.Fatal(err)
   727  		}
   728  		return appendBlob(ocispec.MediaTypeImageIndex, indexJSON)
   729  	}
   730  	descE := appendBlob("layer node E", []byte("Node E is a layer")) // blobs[0], layer "E"
   731  	descF := appendBlob("layer node F", []byte("Node F is a layer")) // blobs[1], layer "F"
   732  	descB := generateManifest(descriptors[0:1]...)                   // blobs[2], manifest "B"
   733  	descC := generateManifest(descriptors[0:2]...)                   // blobs[3], manifest "C"
   734  	descD := generateManifest(descriptors[1:2]...)                   // blobs[4], manifest "D"
   735  	descA := generateIndex(descriptors[2:5]...)                      // blobs[5], index "A"
   736  
   737  	// prepare the content in the fetcher, so that it can be used to test IndexAll
   738  	testContents := []ocispec.Descriptor{descE, descF, descB, descC, descD, descA}
   739  	for i := 0; i < len(blobs); i++ {
   740  		testFetcher.Push(ctx, testContents[i], bytes.NewReader(blobs[i]))
   741  	}
   742  
   743  	// make sure that testFetcher works
   744  	rc, err := testFetcher.Fetch(ctx, descA)
   745  	if err != nil {
   746  		t.Errorf("testFetcher.Fetch() error = %v", err)
   747  	}
   748  	got, err := io.ReadAll(rc)
   749  	if err != nil {
   750  		t.Errorf("testFetcher.Fetch().Read() error = %v", err)
   751  	}
   752  	err = rc.Close()
   753  	if err != nil {
   754  		t.Errorf("testFetcher.Fetch().Close() error = %v", err)
   755  	}
   756  	if !bytes.Equal(got, blobs[5]) {
   757  		t.Errorf("testFetcher.Fetch() = %v, want %v", got, blobs[4])
   758  	}
   759  
   760  	// index node A into testMemory using IndexAll
   761  	testMemory.IndexAll(ctx, testFetcher, descA)
   762  	for i := 0; i < len(blobs); i++ {
   763  		if exists := testMemory.Exists(descriptors[i]); exists != true {
   764  			t.Errorf("digest of blob[%d] should exist in digestSet", i)
   765  		}
   766  	}
   767  }