github.com/exercism/v2-configlet@v3.9.2+incompatible/cmd/lint_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"path/filepath"
     9  	"sort"
    10  	"testing"
    11  
    12  	"github.com/exercism/configlet/track"
    13  	"github.com/exercism/configlet/ui"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  var (
    18  	// The JSON needs to have nulls, not empty strings.
    19  	// We cannot take the address of strings.
    20  	apple   = "apple"
    21  	banana  = "banana"
    22  	unknown = "unknown"
    23  )
    24  
    25  func TestLintTrack(t *testing.T) {
    26  	originalNoHTTP := noHTTP
    27  	noHTTP = true
    28  	defer func() {
    29  		noHTTP = originalNoHTTP
    30  	}()
    31  
    32  	originalOut := ui.Out
    33  	originalErrOut := ui.ErrOut
    34  	ui.Out = ioutil.Discard
    35  	ui.ErrOut = ioutil.Discard
    36  	defer func() {
    37  		ui.Out = originalOut
    38  		ui.ErrOut = originalErrOut
    39  	}()
    40  
    41  	lintTests := []struct {
    42  		desc     string
    43  		path     string
    44  		expected bool
    45  	}{
    46  		{
    47  			desc:     "should fail when given a track containing one or more lint failures.",
    48  			path:     "../fixtures/numbers",
    49  			expected: true,
    50  		},
    51  		{
    52  			desc:     "should fail when given a track containing malformed configuration data.",
    53  			path:     "../fixtures/broken-maintainers",
    54  			expected: true,
    55  		},
    56  		{
    57  			desc:     "should fail when given a track missing READMEs.",
    58  			path:     "../fixtures/missing-readme",
    59  			expected: true,
    60  		},
    61  		{
    62  			desc:     "should not fail when given a track with all of its bits in place.",
    63  			path:     "../fixtures/lint/valid-track",
    64  			expected: false,
    65  		},
    66  	}
    67  
    68  	for _, tt := range lintTests {
    69  		failed := lintTrack(filepath.FromSlash(tt.path))
    70  		assert.Equal(t, tt.expected, failed, tt.desc)
    71  	}
    72  }
    73  
    74  func TestMissingImplementations(t *testing.T) {
    75  	track := track.Track{
    76  		Config: track.Config{
    77  			Exercises: []track.ExerciseMetadata{
    78  				{Slug: "apple"},
    79  				{Slug: "banana"},
    80  				{Slug: "cherry"},
    81  			},
    82  		},
    83  		Exercises: []track.Exercise{
    84  			{Slug: "apple"},
    85  		},
    86  	}
    87  
    88  	slugs := missingImplementations(track)
    89  
    90  	if len(slugs) != 2 {
    91  		t.Fatalf("Expected missing implementations in 2 exercises, missing in %d", len(slugs))
    92  	}
    93  
    94  	sort.Strings(slugs)
    95  
    96  	assert.Equal(t, "banana", slugs[0])
    97  	assert.Equal(t, "cherry", slugs[1])
    98  }
    99  
   100  func TestMissingMetadata(t *testing.T) {
   101  	track := track.Track{
   102  		Config: track.Config{
   103  			Exercises: []track.ExerciseMetadata{
   104  				{Slug: "apple"},
   105  			},
   106  		},
   107  		Exercises: []track.Exercise{
   108  			{Slug: "apple"},
   109  			{Slug: "banana"},
   110  			{Slug: "cherry"},
   111  		},
   112  	}
   113  
   114  	slugs := missingMetadata(track)
   115  
   116  	if len(slugs) != 2 {
   117  		t.Fatalf("Expected missing metadata in 2 exercises, missing in %d", len(slugs))
   118  	}
   119  
   120  	sort.Strings(slugs)
   121  
   122  	assert.Equal(t, "banana", slugs[0])
   123  	assert.Equal(t, "cherry", slugs[1])
   124  }
   125  
   126  func TestMissingReadme(t *testing.T) {
   127  	track := track.Track{
   128  		Exercises: []track.Exercise{
   129  			{Slug: "apple"},
   130  			{Slug: "banana", ReadmePath: "README.md"},
   131  			{Slug: "cherry"},
   132  		},
   133  	}
   134  
   135  	slugs := missingReadme(track)
   136  
   137  	if len(slugs) != 2 {
   138  		t.Fatalf("Expected missing READMEs in 2 exercises, missing in %d", len(slugs))
   139  	}
   140  
   141  	sort.Strings(slugs)
   142  
   143  	assert.Equal(t, "apple", slugs[0])
   144  	assert.Equal(t, "cherry", slugs[1])
   145  }
   146  
   147  func TestMissingSolution(t *testing.T) {
   148  	track := track.Track{
   149  		Exercises: []track.Exercise{
   150  			{Slug: "apple"},
   151  			{Slug: "banana", SolutionPath: "b.txt"},
   152  			{Slug: "cherry"},
   153  		},
   154  	}
   155  
   156  	slugs := missingSolution(track)
   157  
   158  	if len(slugs) != 2 {
   159  		t.Fatalf("Expected missing solutions in 2 exercises, missing in %d", len(slugs))
   160  	}
   161  
   162  	sort.Strings(slugs)
   163  
   164  	assert.Equal(t, "apple", slugs[0])
   165  	assert.Equal(t, "cherry", slugs[1])
   166  }
   167  
   168  func TestMissingTestSuite(t *testing.T) {
   169  	track := track.Track{
   170  		Exercises: []track.Exercise{
   171  			{Slug: "apple"},
   172  			{Slug: "banana", TestSuitePath: "b_test.ext"},
   173  			{Slug: "cherry"},
   174  		},
   175  	}
   176  
   177  	slugs := missingTestSuite(track)
   178  
   179  	if len(slugs) != 2 {
   180  		t.Fatalf("Expected missing test in 2 exercises, missing in %d", len(slugs))
   181  	}
   182  
   183  	sort.Strings(slugs)
   184  
   185  	assert.Equal(t, "apple", slugs[0])
   186  	assert.Equal(t, "cherry", slugs[1])
   187  }
   188  
   189  func TestForegoneViolations(t *testing.T) {
   190  	track := track.Track{
   191  		Config: track.Config{
   192  			ForegoneSlugs: []string{"banana", "cherry"},
   193  		},
   194  		Exercises: []track.Exercise{
   195  			{Slug: "apple"},
   196  			{Slug: "banana"},
   197  			{Slug: "cherry"},
   198  		},
   199  	}
   200  
   201  	slugs := foregoneViolations(track)
   202  
   203  	if len(slugs) != 2 {
   204  		t.Fatalf("Expected foregone violations in 2 exercises, violations in %d", len(slugs))
   205  	}
   206  
   207  	sort.Strings(slugs)
   208  
   209  	assert.Equal(t, "banana", slugs[0])
   210  	assert.Equal(t, "cherry", slugs[1])
   211  }
   212  
   213  func TestDuplicateSlugs(t *testing.T) {
   214  	track := track.Track{
   215  		Config: track.Config{
   216  			Exercises: []track.ExerciseMetadata{
   217  				{Slug: "apple"},
   218  				{Slug: "banana"},
   219  				{Slug: "cherry"},
   220  			},
   221  			DeprecatedSlugs: []string{"apple"},
   222  			ForegoneSlugs:   []string{"banana"},
   223  		},
   224  	}
   225  
   226  	slugs := duplicateSlugs(track)
   227  
   228  	if len(slugs) != 2 {
   229  		t.Fatalf("Expected 2 duplicate slugs, found %d", len(slugs))
   230  	}
   231  
   232  	sort.Strings(slugs)
   233  
   234  	assert.Equal(t, "apple", slugs[0])
   235  	assert.Equal(t, "banana", slugs[1])
   236  }
   237  
   238  func TestDuplicateUUID(t *testing.T) {
   239  	uuidTests := []struct {
   240  		desc     string
   241  		expected int
   242  		config   track.Config
   243  	}{
   244  		{
   245  			desc:     "should not complain about a conflicting UUID for exercises with missing UUIDs.",
   246  			expected: 0,
   247  			config: track.Config{
   248  				Exercises: []track.ExerciseMetadata{
   249  					{Slug: "apple", UUID: ""},
   250  					{Slug: "banana", UUID: ""},
   251  				},
   252  			},
   253  		},
   254  		{
   255  			desc:     "should complain that multiple exercises have a conflicting UUID.",
   256  			expected: 1,
   257  			config: track.Config{
   258  				Exercises: []track.ExerciseMetadata{
   259  					{Slug: "cherry", UUID: "ccc"},
   260  					{Slug: "diakon", UUID: "abc"},
   261  					{Slug: "eggplant", UUID: "ccc"},
   262  				},
   263  			},
   264  		},
   265  	}
   266  
   267  	for _, tt := range uuidTests {
   268  		track := track.Track{Config: tt.config}
   269  		uuids := duplicateUUID(track)
   270  
   271  		assert.Equal(t, tt.expected, len(uuids), tt.desc)
   272  	}
   273  }
   274  
   275  func TestDuplicateTrackUUID(t *testing.T) {
   276  	fakeEndpoint := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   277  		w.WriteHeader(http.StatusConflict)
   278  		fmt.Fprintln(w, `{"uuids": ["ccc"]}`)
   279  	})
   280  
   281  	ts := httptest.NewServer(fakeEndpoint)
   282  	defer ts.Close()
   283  
   284  	originalUUIDValidationURL := UUIDValidationURL
   285  	UUIDValidationURL = ts.URL
   286  	defer func() { UUIDValidationURL = originalUUIDValidationURL }()
   287  
   288  	expected := []string{"ccc"}
   289  	track := track.Track{
   290  		Config: track.Config{
   291  			Exercises: []track.ExerciseMetadata{
   292  				{Slug: "apple", UUID: "abc"},
   293  				{Slug: "banana", UUID: expected[0]},
   294  			},
   295  		},
   296  	}
   297  
   298  	uuids := duplicateTrackUUID(track)
   299  	assert.Equal(t, len(expected), len(uuids))
   300  	assert.Equal(t, expected[0], uuids[0])
   301  
   302  }
   303  
   304  func TestLockedCoreViolation(t *testing.T) {
   305  	trackTests := []struct {
   306  		desc               string
   307  		expectedViolations int
   308  		expectedSlug       string
   309  		config             track.Config
   310  	}{
   311  		{
   312  			desc:               "should have one locked core violation",
   313  			expectedViolations: 1,
   314  			expectedSlug:       "apple",
   315  			config: track.Config{
   316  				Exercises: []track.ExerciseMetadata{
   317  					{
   318  						Slug:       "apple",
   319  						UnlockedBy: &banana,
   320  						IsCore:     true,
   321  					},
   322  					{
   323  						Slug:       "banana",
   324  						UnlockedBy: nil,
   325  						IsCore:     false,
   326  					},
   327  				},
   328  			},
   329  		},
   330  		{
   331  			desc:               "should have no locked core violations",
   332  			expectedViolations: 0,
   333  			expectedSlug:       "",
   334  			config: track.Config{
   335  				Exercises: []track.ExerciseMetadata{
   336  					{
   337  						Slug:       "apple",
   338  						UnlockedBy: nil,
   339  						IsCore:     true,
   340  					},
   341  					{
   342  						Slug:       "banana",
   343  						UnlockedBy: &apple,
   344  						IsCore:     false,
   345  					},
   346  					{
   347  						Slug:       "cherry",
   348  						UnlockedBy: nil,
   349  						IsCore:     false,
   350  					},
   351  				},
   352  			},
   353  		},
   354  	}
   355  
   356  	for _, tt := range trackTests {
   357  		track := track.Track{Config: tt.config}
   358  		slugs := lockedCoreViolation(track)
   359  
   360  		assert.Equal(t, tt.expectedViolations, len(slugs), tt.desc)
   361  
   362  		if len(slugs) > 0 {
   363  			assert.Equal(t, tt.expectedSlug, slugs[0], tt.desc)
   364  		}
   365  	}
   366  }
   367  
   368  func TestUnlockedByViolations(t *testing.T) {
   369  	trackTests := []struct {
   370  		desc               string
   371  		expectedViolations int
   372  		expectedSlug       string
   373  		config             track.Config
   374  	}{
   375  		{
   376  			desc:               "should have one unlocked by violation from non-core exercise",
   377  			expectedViolations: 1,
   378  			expectedSlug:       "banana",
   379  			config: track.Config{
   380  				Exercises: []track.ExerciseMetadata{
   381  					{
   382  						Slug:       "apple",
   383  						UnlockedBy: nil,
   384  						IsCore:     false,
   385  					},
   386  					{
   387  						Slug:       "banana",
   388  						UnlockedBy: &apple,
   389  						IsCore:     false,
   390  					},
   391  				},
   392  			},
   393  		},
   394  		{
   395  			desc:               "should have one unlocked by violation from non-existent exercise",
   396  			expectedViolations: 1,
   397  			expectedSlug:       "cherry",
   398  			config: track.Config{
   399  				Exercises: []track.ExerciseMetadata{
   400  					{
   401  						Slug:       "apple",
   402  						UnlockedBy: nil,
   403  						IsCore:     true,
   404  					},
   405  					{
   406  						Slug:       "banana",
   407  						UnlockedBy: &apple,
   408  						IsCore:     false,
   409  					},
   410  					{
   411  						Slug:       "cherry",
   412  						UnlockedBy: &unknown,
   413  						IsCore:     false,
   414  					},
   415  				},
   416  			},
   417  		},
   418  		{
   419  			desc:               "should have no unlocked by violations",
   420  			expectedViolations: 0,
   421  			expectedSlug:       "",
   422  			config: track.Config{
   423  				Exercises: []track.ExerciseMetadata{
   424  					{
   425  						Slug:       "apple",
   426  						UnlockedBy: nil,
   427  						IsCore:     true,
   428  					},
   429  					{
   430  						Slug:       "banana",
   431  						UnlockedBy: &apple,
   432  						IsCore:     false,
   433  					},
   434  				},
   435  			},
   436  		},
   437  	}
   438  
   439  	for _, tt := range trackTests {
   440  		track := track.Track{Config: tt.config}
   441  		slugs := unlockedByValidExercise(track)
   442  
   443  		assert.Equal(t, tt.expectedViolations, len(slugs), tt.desc)
   444  
   445  		if len(slugs) > 0 {
   446  			assert.Equal(t, tt.expectedSlug, slugs[0], tt.desc)
   447  		}
   448  	}
   449  }