All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 33s
				
			Test / Sandbox (push) Successful in 2m9s
				
			Test / Hakurei (push) Successful in 3m8s
				
			Test / Hpkg (push) Successful in 4m2s
				
			Test / Sandbox (race detector) (push) Successful in 4m7s
				
			Test / Hakurei (race detector) (push) Successful in 4m55s
				
			Test / Flake checks (push) Successful in 1m25s
				
			This reduces collision with local variable names, and generally makes sense for the new store package, since it no longer specifies the state struct. Signed-off-by: Ophestra <cat@gensokyo.uk>
		
			
				
	
	
		
			146 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package store
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/gob"
 | 
						|
	"errors"
 | 
						|
	"io"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"hakurei.app/container/stub"
 | 
						|
	"hakurei.app/hst"
 | 
						|
)
 | 
						|
 | 
						|
func TestEntryData(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	mustEncodeGob := func(e any) string {
 | 
						|
		var buf bytes.Buffer
 | 
						|
		if err := gob.NewEncoder(&buf).Encode(e); err != nil {
 | 
						|
			t.Fatalf("cannot encode invalid state: %v", err)
 | 
						|
			return "\x00" // not reached
 | 
						|
		} else {
 | 
						|
			return buf.String()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	templateStateGob := mustEncodeGob(newTemplateState())
 | 
						|
 | 
						|
	testCases := []struct {
 | 
						|
		name string
 | 
						|
		data string
 | 
						|
		s    *hst.State
 | 
						|
		err  error
 | 
						|
	}{
 | 
						|
		{"invalid header", "\x00\xff\xca\xfe\xff\xff\xff\x00", nil, &hst.AppError{
 | 
						|
			Step: "decode state header", Err: errors.New("unexpected revision ffff")}},
 | 
						|
 | 
						|
		{"invalid gob", "\x00\xff\xca\xfe\x00\x00\xff\x00", nil, &hst.AppError{
 | 
						|
			Step: "decode state body", Err: io.EOF}},
 | 
						|
 | 
						|
		{"invalid config", "\x00\xff\xca\xfe\x00\x00\xff\x00" + mustEncodeGob(new(hst.State)), new(hst.State), &hst.AppError{
 | 
						|
			Step: "validate configuration", Err: hst.ErrConfigNull,
 | 
						|
			Msg: "invalid configuration"}},
 | 
						|
 | 
						|
		{"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, newTemplateState(), &hst.AppError{
 | 
						|
			Step: "validate state enablement", Err: os.ErrInvalid,
 | 
						|
			Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}},
 | 
						|
 | 
						|
		{"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, newTemplateState(), nil},
 | 
						|
	}
 | 
						|
	for _, tc := range testCases {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			t.Parallel()
 | 
						|
 | 
						|
			t.Run("encode", func(t *testing.T) {
 | 
						|
				if tc.s == nil || tc.s.Config == nil {
 | 
						|
					return
 | 
						|
				}
 | 
						|
				t.Parallel()
 | 
						|
 | 
						|
				var buf bytes.Buffer
 | 
						|
				if err := entryEncode(&buf, tc.s); err != nil {
 | 
						|
					t.Fatalf("entryEncode: error = %v", err)
 | 
						|
				}
 | 
						|
 | 
						|
				if tc.err == nil {
 | 
						|
					// Gob encoding is not guaranteed to be deterministic.
 | 
						|
					// While the current implementation mostly is, it has randomised order
 | 
						|
					// for iterating over maps, and hst.Config holds a map for environ.
 | 
						|
					var got hst.State
 | 
						|
					if et, err := entryDecode(&buf, &got); err != nil {
 | 
						|
						t.Fatalf("entryDecode: error = %v", err)
 | 
						|
					} else if stateEt := got.Enablements.Unwrap(); et != stateEt {
 | 
						|
						t.Fatalf("entryDecode: et = %x, state %x", et, stateEt)
 | 
						|
					}
 | 
						|
					if !reflect.DeepEqual(&got, tc.s) {
 | 
						|
						t.Errorf("entryEncode: %x", buf.Bytes())
 | 
						|
					}
 | 
						|
				} else if testing.Verbose() {
 | 
						|
					t.Logf("%x", buf.String())
 | 
						|
				}
 | 
						|
			})
 | 
						|
 | 
						|
			t.Run("decode", func(t *testing.T) {
 | 
						|
				t.Parallel()
 | 
						|
 | 
						|
				var got hst.State
 | 
						|
				if et, err := entryDecode(strings.NewReader(tc.data), &got); !reflect.DeepEqual(err, tc.err) {
 | 
						|
					t.Fatalf("entryDecode: error = %#v, want %#v", err, tc.err)
 | 
						|
				} else if err != nil {
 | 
						|
					return
 | 
						|
				} else if stateEt := got.Enablements.Unwrap(); et != stateEt {
 | 
						|
					t.Fatalf("entryDecode: et = %x, state %x", et, stateEt)
 | 
						|
				}
 | 
						|
 | 
						|
				if !reflect.DeepEqual(&got, tc.s) {
 | 
						|
					t.Errorf("entryDecode: %#v, want %#v", &got, tc.s)
 | 
						|
				}
 | 
						|
			})
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	t.Run("encode fault", func(t *testing.T) {
 | 
						|
		t.Parallel()
 | 
						|
		s := newTemplateState()
 | 
						|
 | 
						|
		t.Run("gob", func(t *testing.T) {
 | 
						|
			var want = &hst.AppError{Step: "encode state body", Err: stub.UniqueError(0xcafe)}
 | 
						|
			if err := entryEncode(stubNErrorWriter(entryHeaderSize), s); !reflect.DeepEqual(err, want) {
 | 
						|
				t.Errorf("entryEncode: error = %#v, want %#v", err, want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
 | 
						|
		t.Run("header", func(t *testing.T) {
 | 
						|
			var want = &hst.AppError{Step: "encode state header", Err: stub.UniqueError(0xcafe)}
 | 
						|
			if err := entryEncode(stubNErrorWriter(entryHeaderSize-1), s); !reflect.DeepEqual(err, want) {
 | 
						|
				t.Errorf("entryEncode: error = %#v, want %#v", err, want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// newTemplateState returns the address of a new template [hst.State] struct.
 | 
						|
func newTemplateState() *hst.State {
 | 
						|
	return &hst.State{
 | 
						|
		ID:      hst.ID(bytes.Repeat([]byte{0xaa}, len(hst.ID{}))),
 | 
						|
		PID:     0xcafebabe,
 | 
						|
		ShimPID: 0xdeadbeef,
 | 
						|
		Config:  hst.Template(),
 | 
						|
		Time:    time.Unix(0, 0),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// stubNErrorWriter returns an error for writes above a certain size.
 | 
						|
type stubNErrorWriter int
 | 
						|
 | 
						|
func (w stubNErrorWriter) Write(p []byte) (n int, err error) {
 | 
						|
	if len(p) > int(w) {
 | 
						|
		return int(w), stub.UniqueError(0xcafe)
 | 
						|
	}
 | 
						|
	return io.Discard.Write(p)
 | 
						|
}
 |