github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/gnorkle/feeds/static/feed_test.gno (about)

     1  package static_test
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"gno.land/p/demo/gnorkle/feed"
     8  	"gno.land/p/demo/gnorkle/feeds/static"
     9  	"gno.land/p/demo/gnorkle/gnorkle"
    10  	"gno.land/p/demo/gnorkle/ingester"
    11  	"gno.land/p/demo/gnorkle/message"
    12  	"gno.land/p/demo/gnorkle/storage/simple"
    13  )
    14  
    15  type mockIngester struct {
    16  	canAutoCommit   bool
    17  	ingestErr       error
    18  	commitErr       error
    19  	value           string
    20  	providerAddress string
    21  }
    22  
    23  func (i mockIngester) Type() ingester.Type {
    24  	return ingester.Type(0)
    25  }
    26  
    27  func (i *mockIngester) Ingest(value, providerAddress string) (bool, error) {
    28  	if i.ingestErr != nil {
    29  		return false, i.ingestErr
    30  	}
    31  
    32  	i.value = value
    33  	i.providerAddress = providerAddress
    34  	return i.canAutoCommit, nil
    35  }
    36  
    37  func (i *mockIngester) CommitValue(storage gnorkle.Storage, providerAddress string) error {
    38  	if i.commitErr != nil {
    39  		return i.commitErr
    40  	}
    41  
    42  	return storage.Put(i.value)
    43  }
    44  
    45  func TestNewSingleValueFeed(t *testing.T) {
    46  	staticFeed := static.NewSingleValueFeed("1", "")
    47  	if staticFeed.ID() != "1" {
    48  		t.Errorf("expected ID to be 1, got %s", staticFeed.ID())
    49  	}
    50  
    51  	if staticFeed.Type() != feed.TypeStatic {
    52  		t.Errorf("expected static feed type, got %s", staticFeed.Type())
    53  	}
    54  }
    55  
    56  func TestFeed_Ingest(t *testing.T) {
    57  	var undefinedFeed *static.Feed
    58  	if undefinedFeed.Ingest("", "", "") != feed.ErrUndefined {
    59  		t.Errorf("expected ErrUndefined, got nil")
    60  	}
    61  
    62  	tests := []struct {
    63  		name               string
    64  		ingester           *mockIngester
    65  		verifyIsLocked     bool
    66  		doCommit           bool
    67  		funcType           message.FuncType
    68  		msg                string
    69  		providerAddress    string
    70  		expFeedValueString string
    71  		expErrText         string
    72  		expIsActive        bool
    73  	}{
    74  		{
    75  			name:        "func invalid error",
    76  			ingester:    &mockIngester{},
    77  			funcType:    message.FuncType("derp"),
    78  			expErrText:  "invalid message function derp",
    79  			expIsActive: true,
    80  		},
    81  		{
    82  			name: "func ingest ingest error",
    83  			ingester: &mockIngester{
    84  				ingestErr: errors.New("ingest error"),
    85  			},
    86  			funcType:    message.FuncTypeIngest,
    87  			expErrText:  "ingest error",
    88  			expIsActive: true,
    89  		},
    90  		{
    91  			name: "func ingest commit error",
    92  			ingester: &mockIngester{
    93  				commitErr:     errors.New("commit error"),
    94  				canAutoCommit: true,
    95  			},
    96  			funcType:    message.FuncTypeIngest,
    97  			expErrText:  "commit error",
    98  			expIsActive: true,
    99  		},
   100  		{
   101  			name: "func commit commit error",
   102  			ingester: &mockIngester{
   103  				commitErr:     errors.New("commit error"),
   104  				canAutoCommit: true,
   105  			},
   106  			funcType:    message.FuncTypeCommit,
   107  			expErrText:  "commit error",
   108  			expIsActive: true,
   109  		},
   110  		{
   111  			name:            "only ingest",
   112  			ingester:        &mockIngester{},
   113  			funcType:        message.FuncTypeIngest,
   114  			msg:             "still active feed",
   115  			providerAddress: "gno1234",
   116  			expIsActive:     true,
   117  		},
   118  		{
   119  			name:               "ingest autocommit",
   120  			ingester:           &mockIngester{canAutoCommit: true},
   121  			funcType:           message.FuncTypeIngest,
   122  			msg:                "still active feed",
   123  			providerAddress:    "gno1234",
   124  			expFeedValueString: "still active feed",
   125  			verifyIsLocked:     true,
   126  		},
   127  		{
   128  			name:           "commit no value",
   129  			ingester:       &mockIngester{},
   130  			funcType:       message.FuncTypeCommit,
   131  			msg:            "shouldn't be stored",
   132  			verifyIsLocked: true,
   133  		},
   134  		{
   135  			name:               "ingest then commmit",
   136  			ingester:           &mockIngester{},
   137  			funcType:           message.FuncTypeIngest,
   138  			msg:                "blahblah",
   139  			doCommit:           true,
   140  			expFeedValueString: "blahblah",
   141  			verifyIsLocked:     true,
   142  		},
   143  	}
   144  
   145  	for _, tt := range tests {
   146  		t.Run(tt.name, func(t *testing.T) {
   147  			staticFeed := static.NewFeed(
   148  				"1",
   149  				"string",
   150  				tt.ingester,
   151  				simple.NewStorage(1),
   152  				nil,
   153  			)
   154  
   155  			var errText string
   156  			if err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {
   157  				errText = err.Error()
   158  			}
   159  
   160  			if errText != tt.expErrText {
   161  				t.Fatalf("expected error text %s, got %s", tt.expErrText, errText)
   162  			}
   163  
   164  			if tt.doCommit {
   165  				err := staticFeed.Ingest(message.FuncTypeCommit, "", "")
   166  				if err != nil {
   167  					t.Fatalf("follow up commit failed: %s", err.Error())
   168  				}
   169  			}
   170  
   171  			if tt.verifyIsLocked {
   172  				errText = ""
   173  				if err := staticFeed.Ingest(tt.funcType, tt.msg, tt.providerAddress); err != nil {
   174  					errText = err.Error()
   175  				}
   176  
   177  				if errText != "feed locked" {
   178  					t.Fatalf("expected error text feed locked, got %s", errText)
   179  				}
   180  			}
   181  
   182  			if tt.ingester.providerAddress != tt.providerAddress {
   183  				t.Errorf("expected provider address %s, got %s", tt.providerAddress, tt.ingester.providerAddress)
   184  			}
   185  
   186  			feedValue, dataType, isLocked := staticFeed.Value()
   187  			if feedValue.String != tt.expFeedValueString {
   188  				t.Errorf("expected feed value string %s, got %s", tt.expFeedValueString, feedValue.String)
   189  			}
   190  
   191  			if dataType != "string" {
   192  				t.Errorf("expected data type string, got %s", dataType)
   193  			}
   194  
   195  			if isLocked != tt.verifyIsLocked {
   196  				t.Errorf("expected is locked %t, got %t", tt.verifyIsLocked, isLocked)
   197  			}
   198  
   199  			if staticFeed.IsActive() != tt.expIsActive {
   200  				t.Errorf("expected is active %t, got %t", tt.expIsActive, staticFeed.IsActive())
   201  			}
   202  		})
   203  	}
   204  }
   205  
   206  type mockTask struct {
   207  	err   error
   208  	value string
   209  }
   210  
   211  func (t mockTask) MarshalJSON() ([]byte, error) {
   212  	if t.err != nil {
   213  		return nil, t.err
   214  	}
   215  
   216  	return []byte(`{"value":"` + t.value + `"}`), nil
   217  }
   218  
   219  func TestFeed_Tasks(t *testing.T) {
   220  	id := "99"
   221  	valueDataType := "int"
   222  
   223  	tests := []struct {
   224  		name       string
   225  		tasks      []feed.Task
   226  		expErrText string
   227  		expJSON    string
   228  	}{
   229  		{
   230  			name:    "no tasks",
   231  			expJSON: `{"id":"99","type":"0","value_type":"int","tasks":[]}`,
   232  		},
   233  		{
   234  			name: "marshal error",
   235  			tasks: []feed.Task{
   236  				mockTask{err: errors.New("marshal error")},
   237  			},
   238  			expErrText: "marshal error",
   239  		},
   240  		{
   241  			name: "one task",
   242  			tasks: []feed.Task{
   243  				mockTask{value: "single"},
   244  			},
   245  			expJSON: `{"id":"99","type":"0","value_type":"int","tasks":[{"value":"single"}]}`,
   246  		},
   247  		{
   248  			name: "two tasks",
   249  			tasks: []feed.Task{
   250  				mockTask{value: "first"},
   251  				mockTask{value: "second"},
   252  			},
   253  			expJSON: `{"id":"99","type":"0","value_type":"int","tasks":[{"value":"first"},{"value":"second"}]}`,
   254  		},
   255  	}
   256  
   257  	for _, tt := range tests {
   258  		t.Run(tt.name, func(t *testing.T) {
   259  			staticFeed := static.NewSingleValueFeed(
   260  				id,
   261  				valueDataType,
   262  				tt.tasks...,
   263  			)
   264  
   265  			if len(staticFeed.Tasks()) != len(tt.tasks) {
   266  				t.Fatalf("expected %d tasks, got %d", len(tt.tasks), len(staticFeed.Tasks()))
   267  			}
   268  
   269  			var errText string
   270  			json, err := staticFeed.MarshalJSON()
   271  			if err != nil {
   272  				errText = err.Error()
   273  			}
   274  
   275  			if errText != tt.expErrText {
   276  				t.Fatalf("expected error text %s, got %s", tt.expErrText, errText)
   277  			}
   278  
   279  			if string(json) != tt.expJSON {
   280  				t.Errorf("expected json %s, got %s", tt.expJSON, string(json))
   281  			}
   282  		})
   283  	}
   284  }