github.com/grailbio/base@v0.0.11/cloud/spotfeed/querier_test.go (about)

     1  package spotfeed
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  )
     7  
     8  func TestQuerier(t *testing.T) {
     9  	now := time.Now().Truncate(time.Second)
    10  	iid, typ := "some-instance-id", "some-instance-type"
    11  	entries := []*Entry{
    12  		{ChargeUSD: 60, Timestamp: now.Add(-60 * time.Minute), InstanceID: iid, Instance: typ},
    13  		{ChargeUSD: 110 /*ignored*/, Timestamp: now, InstanceID: iid, Instance: typ},
    14  		{ChargeUSD: 120, Timestamp: now, InstanceID: iid, Instance: typ},
    15  		{ChargeUSD: 80 /*ignored*/, Timestamp: now.Add(59 * time.Minute), InstanceID: iid, Instance: typ},
    16  		{ChargeUSD: 90, Timestamp: now.Add(59 * time.Minute), InstanceID: iid, Instance: typ},
    17  		{ChargeUSD: 120, Timestamp: now.Add(121 * time.Minute), InstanceID: iid, Instance: typ},
    18  		{ChargeUSD: 88 /*ignored*/, Timestamp: now.Add(3 * time.Hour), InstanceID: iid, Instance: typ},
    19  		{ChargeUSD: 89 /*ignored*/, Timestamp: now.Add(3 * time.Hour), InstanceID: iid, Instance: typ},
    20  		{ChargeUSD: 90 /*duplicate*/, Timestamp: now.Add(3 * time.Hour), InstanceID: iid, Instance: typ},
    21  		{ChargeUSD: 90, Timestamp: now.Add(3 * time.Hour), InstanceID: iid, Instance: typ},
    22  	}
    23  	terminated := now.Add(3*time.Hour + 30*time.Minute)
    24  	q := newQuerier(entries)
    25  	_, err := q.Query("some-other-instance-id", Period{}, time.Time{})
    26  	if got, want := err, ErrMissingData; got != want {
    27  		t.Errorf("got %v, want %v", got, want)
    28  	}
    29  	for i, tt := range []struct {
    30  		iet   time.Time
    31  		p     Period
    32  		c     Cost
    33  		wantE error
    34  	}{
    35  		{ // Period starting and ending before data.
    36  			time.Time{},
    37  			Period{now.Add(-90 * time.Minute), now.Add(-70 * time.Minute)},
    38  			Cost{},
    39  			ErrMissingData,
    40  		},
    41  		{ // Period starting and ending after data.
    42  			time.Time{},
    43  			Period{now.Add(3*time.Hour + 1*time.Minute), now.Add(4 * time.Hour)},
    44  			Cost{},
    45  			ErrMissingData,
    46  		},
    47  		{ // Period starting before data but ending within.
    48  			time.Time{},
    49  			Period{now.Add(-90 * time.Minute), now.Add(-30 * time.Minute)},
    50  			Cost{
    51  				Period{now.Add(-60 * time.Minute), now.Add(-30 * time.Minute)},
    52  				60 * 30.0 / 60.0,
    53  			},
    54  			nil,
    55  		},
    56  		{ // Period starting within data and going beyond.
    57  			terminated,
    58  			Period{now.Add(2 * time.Hour), now.Add(4 * time.Hour)},
    59  			Cost{
    60  				Period{now.Add(2 * time.Hour), terminated},
    61  				90*1.0/62.0 + 120 + 90,
    62  			},
    63  			nil,
    64  		},
    65  		{ // Period starting within data and going beyond with no terminated
    66  			time.Time{},
    67  			Period{now.Add(2 * time.Hour), now.Add(4 * time.Hour)},
    68  			Cost{
    69  				Period{now.Add(2 * time.Hour), now.Add(4 * time.Hour)},
    70  				90*1.0/62.0 + 120 + 90,
    71  			},
    72  			nil,
    73  		},
    74  		{ // Period starting exactly at some timestamp and ending within its period.
    75  			time.Time{},
    76  			Period{now, now.Add(5 * time.Minute)},
    77  			Cost{
    78  				Period{now, now.Add(5 * time.Minute)},
    79  				120 * 5.0 / 59.0,
    80  			},
    81  			nil,
    82  		},
    83  		{ // Period starting and within a single time period.
    84  			time.Time{},
    85  			Period{now.Add(1 * time.Minute), now.Add(6 * time.Minute)},
    86  			Cost{
    87  				Period{now.Add(1 * time.Minute), now.Add(6 * time.Minute)},
    88  				120 * 5.0 / 59.0,
    89  			},
    90  			nil,
    91  		},
    92  		{ // Period starting exactly at some timestamp and spanning more than one.
    93  			time.Time{},
    94  			Period{now, now.Add(80 * time.Minute)},
    95  			Cost{
    96  				Period{now, now.Add(80 * time.Minute)},
    97  				120 + 90*21.0/62.0,
    98  			},
    99  			nil,
   100  		},
   101  		{ // Period starting before data and ending after.
   102  			terminated,
   103  			Period{now.Add(-90 * time.Minute), now.Add(6 * time.Hour)},
   104  			Cost{
   105  				Period{now.Add(-60 * time.Minute), terminated},
   106  				60 + 120 + 90 + 120 + 90,
   107  			},
   108  			nil,
   109  		},
   110  		{ // Period starting before data and ending after with no terminated.
   111  			time.Time{},
   112  			Period{now.Add(-90 * time.Minute), now.Add(6 * time.Hour)},
   113  			Cost{
   114  				Period{now.Add(-60 * time.Minute), now.Add(6 * time.Hour)},
   115  				60 + 120 + 90 + 120 + 90,
   116  			},
   117  			nil,
   118  		},
   119  		{ // Period starting within data but ending within last period before instance end time.
   120  			terminated,
   121  			Period{now.Add(-90 * time.Minute), now.Add(3*time.Hour + 15*time.Minute)},
   122  			Cost{
   123  				Period{now.Add(-60 * time.Minute), now.Add(3*time.Hour + 15*time.Minute)},
   124  				60 + 120 + 90 + 120 + 90*15/30.0,
   125  			},
   126  			nil,
   127  		},
   128  		{ // Period starting within data but ending within last period with no terminated.
   129  			time.Time{},
   130  			Period{now.Add(-90 * time.Minute), now.Add(3*time.Hour + 15*time.Minute)},
   131  			Cost{
   132  				Period{now.Add(-60 * time.Minute), now.Add(3*time.Hour + 15*time.Minute)},
   133  				60 + 120 + 90 + 120 + 90,
   134  			},
   135  			nil,
   136  		},
   137  	} {
   138  		c, err := q.Query(iid, tt.p, tt.iet)
   139  		if tt.wantE != nil {
   140  			if got, want := err, tt.wantE; got != want {
   141  				t.Errorf("[%d] got %v, want %v", i, got, want)
   142  			}
   143  			continue
   144  		}
   145  		if err != nil {
   146  			t.Error(err)
   147  			continue
   148  		}
   149  		if got, want := c, tt.c; got != want {
   150  			t.Errorf("[%d[ got %v, want %v", i, got, want)
   151  		}
   152  	}
   153  }