get.porter.sh/porter@v1.3.0/pkg/pkgmgmt/feed/generate_test.go (about)

     1  package feed
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"testing"
     9  	"time"
    10  
    11  	"get.porter.sh/porter/pkg/portercontext"
    12  	"get.porter.sh/porter/pkg/test"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestGenerate(t *testing.T) {
    18  	var err error
    19  	ctx := context.Background()
    20  	tc := portercontext.NewTestContext(t)
    21  	tc.AddTestFile("testdata/atom-template.xml", "template.xml")
    22  
    23  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-darwin-amd64")
    24  	if err != nil {
    25  		require.NoError(t, err)
    26  	}
    27  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-darwin-arm64")
    28  	if err != nil {
    29  		require.NoError(t, err)
    30  	}
    31  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-linux-amd64")
    32  	if err != nil {
    33  		require.NoError(t, err)
    34  	}
    35  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-linux-arm64")
    36  	if err != nil {
    37  		require.NoError(t, err)
    38  	}
    39  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-windows-amd64.exe")
    40  	if err != nil {
    41  		require.NoError(t, err)
    42  	}
    43  	_, err = tc.FileSystem.Create("bin/v1.2.3/helm-windows-arm64.exe")
    44  	if err != nil {
    45  		require.NoError(t, err)
    46  	}
    47  
    48  	// Force the up3 timestamps to stay the same for each test run
    49  	up3, _ := time.Parse("2006-Jan-02", "2013-Feb-03")
    50  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-darwin-amd64", up3, up3)
    51  	if err != nil {
    52  		require.NoError(t, err)
    53  	}
    54  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-darwin-arm64", up3, up3)
    55  	if err != nil {
    56  		require.NoError(t, err)
    57  	}
    58  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-linux-amd64", up3, up3)
    59  	if err != nil {
    60  		require.NoError(t, err)
    61  	}
    62  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-linux-arm64", up3, up3)
    63  	if err != nil {
    64  		require.NoError(t, err)
    65  	}
    66  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-windows-amd64.exe", up3, up3)
    67  	if err != nil {
    68  		require.NoError(t, err)
    69  	}
    70  	err = tc.FileSystem.Chtimes("bin/v1.2.3/helm-windows-arm64.exe", up3, up3)
    71  	if err != nil {
    72  		require.NoError(t, err)
    73  	}
    74  
    75  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-darwin-amd64")
    76  	if err != nil {
    77  		require.NoError(t, err)
    78  	}
    79  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-linux-amd64")
    80  	if err != nil {
    81  		require.NoError(t, err)
    82  	}
    83  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-windows-amd64.exe")
    84  	if err != nil {
    85  		require.NoError(t, err)
    86  	}
    87  
    88  	up4, _ := time.Parse("2006-Jan-02", "2013-Feb-04")
    89  	err = tc.FileSystem.Chtimes("bin/v1.2.4/helm-darwin-amd64", up4, up4)
    90  	if err != nil {
    91  		require.NoError(t, err)
    92  	}
    93  	err = tc.FileSystem.Chtimes("bin/v1.2.4/helm-linux-amd64", up4, up4)
    94  	if err != nil {
    95  		require.NoError(t, err)
    96  	}
    97  	err = tc.FileSystem.Chtimes("bin/v1.2.4/helm-windows-amd64.exe", up4, up4)
    98  	if err != nil {
    99  		require.NoError(t, err)
   100  	}
   101  
   102  	_, err = tc.FileSystem.Create("bin/v1.2.3/exec-darwin-amd64")
   103  	if err != nil {
   104  		require.NoError(t, err)
   105  	}
   106  	_, err = tc.FileSystem.Create("bin/v1.2.3/exec-linux-amd64")
   107  	if err != nil {
   108  		require.NoError(t, err)
   109  	}
   110  	_, err = tc.FileSystem.Create("bin/v1.2.3/exec-windows-amd64.exe")
   111  	if err != nil {
   112  		require.NoError(t, err)
   113  	}
   114  
   115  	up2, _ := time.Parse("2006-Jan-02", "2013-Feb-02")
   116  	err = tc.FileSystem.Chtimes("bin/v1.2.3/exec-darwin-amd64", up2, up2)
   117  	if err != nil {
   118  		require.NoError(t, err)
   119  	}
   120  	err = tc.FileSystem.Chtimes("bin/v1.2.3/exec-linux-amd64", up2, up2)
   121  	if err != nil {
   122  		require.NoError(t, err)
   123  	}
   124  	err = tc.FileSystem.Chtimes("bin/v1.2.3/exec-windows-amd64.exe", up2, up2)
   125  	if err != nil {
   126  		require.NoError(t, err)
   127  	}
   128  
   129  	_, err = tc.FileSystem.Create("bin/canary/exec-darwin-amd64")
   130  	if err != nil {
   131  		require.NoError(t, err)
   132  	}
   133  	_, err = tc.FileSystem.Create("bin/canary/exec-linux-amd64")
   134  	if err != nil {
   135  		require.NoError(t, err)
   136  	}
   137  	_, err = tc.FileSystem.Create("bin/canary/exec-windows-amd64.exe")
   138  	if err != nil {
   139  		require.NoError(t, err)
   140  	}
   141  
   142  	up10, _ := time.Parse("2006-Jan-02", "2013-Feb-10")
   143  	err = tc.FileSystem.Chtimes("bin/canary/exec-darwin-amd64", up10, up10)
   144  	if err != nil {
   145  		require.NoError(t, err)
   146  	}
   147  	err = tc.FileSystem.Chtimes("bin/canary/exec-linux-amd64", up10, up10)
   148  	if err != nil {
   149  		require.NoError(t, err)
   150  	}
   151  	err = tc.FileSystem.Chtimes("bin/canary/exec-windows-amd64.exe", up10, up10)
   152  	if err != nil {
   153  		require.NoError(t, err)
   154  	}
   155  
   156  	// Create extraneous release directories that should be ignored
   157  	_, err = tc.FileSystem.Create("bin/v0.34.0-1-gda/helm-darwin-amd64")
   158  	if err != nil {
   159  		require.NoError(t, err)
   160  	}
   161  	_, err = tc.FileSystem.Create("bin/v0.34.0-2-g1234567/helm-linux-amd64")
   162  	if err != nil {
   163  		require.NoError(t, err)
   164  	}
   165  	_, err = tc.FileSystem.Create("bin/v0.34.0-3-g12345/helm-windows-amd64.exe")
   166  	if err != nil {
   167  		require.NoError(t, err)
   168  	}
   169  
   170  	_, err = tc.FileSystem.Create("bin/latest/helm-darwin-amd64")
   171  	if err != nil {
   172  		require.NoError(t, err)
   173  	}
   174  	_, err = tc.FileSystem.Create("bin/latest/helm-linux-amd64")
   175  	if err != nil {
   176  		require.NoError(t, err)
   177  	}
   178  	_, err = tc.FileSystem.Create("bin/latest/helm-windows-amd64.exe")
   179  	if err != nil {
   180  		require.NoError(t, err)
   181  	}
   182  
   183  	_, err = tc.FileSystem.Create("bin/canary-v1/exec-darwin-amd64")
   184  	if err != nil {
   185  		require.NoError(t, err)
   186  	}
   187  	_, err = tc.FileSystem.Create("bin/canary-v1/exec-linux-amd64")
   188  	if err != nil {
   189  		require.NoError(t, err)
   190  	}
   191  	_, err = tc.FileSystem.Create("bin/canary-v1/exec-windows-amd64.exe")
   192  	if err != nil {
   193  		require.NoError(t, err)
   194  	}
   195  
   196  	opts := GenerateOptions{
   197  		AtomFile:        "atom.xml",
   198  		SearchDirectory: "bin",
   199  		TemplateFile:    "template.xml",
   200  	}
   201  	f := NewMixinFeed(tc.Context)
   202  	err = f.Generate(ctx, opts)
   203  	require.NoError(t, err)
   204  	err = f.Save(opts)
   205  	require.NoError(t, err)
   206  
   207  	b, err := tc.FileSystem.ReadFile("atom.xml")
   208  	require.NoError(t, err)
   209  	gotXml := string(b)
   210  	test.CompareGoldenFile(t, "testdata/atom.xml", gotXml)
   211  }
   212  
   213  func TestShouldPublishVersion(t *testing.T) {
   214  	testcases := map[string]bool{
   215  		"canary":                    true,
   216  		"latest":                    false,
   217  		"dev":                       false,
   218  		"rando-thing":               false,
   219  		"v1.2.3":                    true,
   220  		"v1.2.3-alpha.1":            true,
   221  		"v1.2.3-alpha.1+metadata":   true,
   222  		"v1.2.3-alpha.1-1-ga368f9f": false,
   223  		"v1.2.3-0-g":                true, // Need the hash
   224  		"v0.33.2-0-ga":              false,
   225  		"v0.33.2-0-g12345678":       false,
   226  		"v0.33.2-0-ga1b3c5":         false,
   227  	}
   228  
   229  	for version, wantPublish := range testcases {
   230  		t.Run(version, func(t *testing.T) {
   231  			gotPublish := shouldPublishVersion(version)
   232  			assert.Equal(t, wantPublish, gotPublish)
   233  		})
   234  	}
   235  }
   236  
   237  func TestGenerate_RegexMatch(t *testing.T) {
   238  	testcases := []struct {
   239  		name      string
   240  		mixinName string
   241  		wantError string
   242  	}{{
   243  		name:      "no bins",
   244  		mixinName: "",
   245  		wantError: `file does not exist`,
   246  	}, {
   247  		name:      "valid mixin name",
   248  		mixinName: "my-42nd-mixin",
   249  		wantError: "",
   250  	}, {
   251  		name:      "invalid mixin name",
   252  		mixinName: "my-42nd-mixin!",
   253  		wantError: `no mixin binaries found in bin matching the regex`,
   254  	}}
   255  
   256  	for _, tc := range testcases {
   257  		t.Run(tc.name, func(t *testing.T) {
   258  			ctx := context.Background()
   259  			porterCtx := portercontext.NewTestContext(t)
   260  			porterCtx.AddTestFile("testdata/atom-template.xml", "template.xml")
   261  
   262  			if tc.mixinName != "" {
   263  				var err error
   264  				_, err = porterCtx.FileSystem.Create(fmt.Sprintf("bin/v1.2.3/%s-darwin-amd64", tc.mixinName))
   265  				if err != nil {
   266  					require.NoError(t, err)
   267  				}
   268  				_, err = porterCtx.FileSystem.Create(fmt.Sprintf("bin/v1.2.3/%s-linux-amd64", tc.mixinName))
   269  				if err != nil {
   270  					require.NoError(t, err)
   271  				}
   272  				_, err = porterCtx.FileSystem.Create(fmt.Sprintf("bin/v1.2.3/%s-windows-amd64.exe", tc.mixinName))
   273  				if err != nil {
   274  					require.NoError(t, err)
   275  				}
   276  			}
   277  
   278  			opts := GenerateOptions{
   279  				AtomFile:        "atom.xml",
   280  				SearchDirectory: "bin",
   281  				TemplateFile:    "template.xml",
   282  			}
   283  			f := NewMixinFeed(porterCtx.Context)
   284  			err := f.Generate(ctx, opts)
   285  			if tc.wantError != "" {
   286  				require.ErrorContains(t, err, tc.wantError)
   287  			} else {
   288  				require.NoError(t, err)
   289  			}
   290  		})
   291  	}
   292  }
   293  
   294  func TestGenerate_ExistingFeed(t *testing.T) {
   295  	ctx := context.Background()
   296  	tc := portercontext.NewTestContext(t)
   297  	tc.AddTestFile("testdata/atom-template.xml", "template.xml")
   298  	tc.AddTestFile("testdata/atom-existing.xml", "atom.xml")
   299  
   300  	_, err := tc.FileSystem.Create("bin/v1.2.4/helm-darwin-amd64")
   301  	require.NoError(t, err)
   302  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-linux-amd64")
   303  	require.NoError(t, err)
   304  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-windows-amd64.exe")
   305  	require.NoError(t, err)
   306  
   307  	up4, _ := time.Parse("2006-Jan-02", "2013-Feb-04")
   308  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-darwin-amd64", up4, up4))
   309  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-linux-amd64", up4, up4))
   310  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-windows-amd64.exe", up4, up4))
   311  
   312  	_, err = tc.FileSystem.Create("bin/canary/exec-darwin-amd64")
   313  	require.NoError(t, err)
   314  	_, err = tc.FileSystem.Create("bin/canary/exec-linux-amd64")
   315  	require.NoError(t, err)
   316  	_, err = tc.FileSystem.Create("bin/canary/exec-windows-amd64.exe")
   317  	require.NoError(t, err)
   318  
   319  	up10, _ := time.Parse("2006-Jan-02", "2013-Feb-10")
   320  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-darwin-amd64", up10, up10))
   321  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-linux-amd64", up10, up10))
   322  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-windows-amd64.exe", up10, up10))
   323  
   324  	opts := GenerateOptions{
   325  		AtomFile:        "atom.xml",
   326  		SearchDirectory: "bin",
   327  		TemplateFile:    "template.xml",
   328  	}
   329  	f := NewMixinFeed(tc.Context)
   330  	err = f.Generate(ctx, opts)
   331  	require.NoError(t, err)
   332  	err = f.Save(opts)
   333  	require.NoError(t, err)
   334  
   335  	b, err := tc.FileSystem.ReadFile("atom.xml")
   336  	require.NoError(t, err)
   337  	gotXml := string(b)
   338  
   339  	b, err = os.ReadFile("testdata/atom.xml")
   340  	require.NoError(t, err)
   341  	wantXml := string(b)
   342  
   343  	assert.Equal(t, wantXml, gotXml)
   344  }
   345  
   346  func TestGenerate_RegenerateDoesNotCreateDuplicates(t *testing.T) {
   347  	ctx := context.Background()
   348  	tc := portercontext.NewTestContext(t)
   349  	tc.AddTestFile("testdata/atom-template.xml", "template.xml")
   350  	tc.AddTestFile("testdata/atom-existing.xml", "atom.xml")
   351  
   352  	_, err := tc.FileSystem.Create("bin/v1.2.4/helm-darwin-amd64")
   353  	require.NoError(t, err)
   354  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-linux-amd64")
   355  	require.NoError(t, err)
   356  	_, err = tc.FileSystem.Create("bin/v1.2.4/helm-windows-amd64.exe")
   357  	require.NoError(t, err)
   358  
   359  	up4, _ := time.Parse("2006-Jan-02", "2013-Feb-04")
   360  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-darwin-amd64", up4, up4))
   361  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-linux-amd64", up4, up4))
   362  	require.NoError(t, tc.FileSystem.Chtimes("bin/v1.2.4/helm-windows-amd64.exe", up4, up4))
   363  
   364  	_, err = tc.FileSystem.Create("bin/canary/exec-darwin-amd64")
   365  	require.NoError(t, err)
   366  	_, err = tc.FileSystem.Create("bin/canary/exec-linux-amd64")
   367  	require.NoError(t, err)
   368  	_, err = tc.FileSystem.Create("bin/canary/exec-windows-amd64.exe")
   369  	require.NoError(t, err)
   370  
   371  	up10, _ := time.Parse("2006-Jan-02", "2013-Feb-10")
   372  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-darwin-amd64", up10, up10))
   373  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-linux-amd64", up10, up10))
   374  	require.NoError(t, tc.FileSystem.Chtimes("bin/canary/exec-windows-amd64.exe", up10, up10))
   375  
   376  	opts := GenerateOptions{
   377  		AtomFile:        "atom.xml",
   378  		SearchDirectory: "bin",
   379  		TemplateFile:    "template.xml",
   380  	}
   381  	f := NewMixinFeed(tc.Context)
   382  
   383  	err = f.Generate(ctx, opts)
   384  	require.NoError(t, err)
   385  	err = f.Save(opts)
   386  	require.NoError(t, err)
   387  
   388  	// Run the generation again, against the same versions, and make sure they don't insert duplicate files
   389  	// This mimics what the CI does when we repeat a build, or have multiple
   390  	// canary builds on the "main" branch
   391  	err = f.Generate(ctx, opts)
   392  	require.NoError(t, err)
   393  	err = f.Save(opts)
   394  	require.NoError(t, err)
   395  
   396  	b, err := tc.FileSystem.ReadFile("atom.xml")
   397  	require.NoError(t, err)
   398  	gotXml := string(b)
   399  
   400  	b, err = os.ReadFile("testdata/atom.xml")
   401  	require.NoError(t, err)
   402  	wantXml := string(b)
   403  
   404  	assert.Equal(t, wantXml, gotXml)
   405  }
   406  
   407  func TestMixinEntries_Sort(t *testing.T) {
   408  	up2, _ := time.Parse("2006-Jan-02", "2013-Feb-02")
   409  	up3, _ := time.Parse("2006-Jan-02", "2013-Feb-03")
   410  	up4, _ := time.Parse("2006-Jan-02", "2013-Feb-04")
   411  
   412  	entries := MixinEntries{
   413  		{
   414  			Files: []*MixinFile{
   415  				{Updated: up3},
   416  			},
   417  		},
   418  		{
   419  			Files: []*MixinFile{
   420  				{Updated: up2},
   421  			},
   422  		},
   423  		{
   424  			Files: []*MixinFile{
   425  				{Updated: up4},
   426  			},
   427  		},
   428  	}
   429  
   430  	sort.Sort(sort.Reverse(entries))
   431  
   432  	assert.Equal(t, up4, entries[0].Files[0].Updated)
   433  	assert.Equal(t, up3, entries[1].Files[0].Updated)
   434  	assert.Equal(t, up2, entries[2].Files[0].Updated)
   435  }