github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/compose/schema/schema_test.go (about)

     1  // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
     2  //go:build go1.19
     3  
     4  package schema
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"gotest.tools/v3/assert"
    11  )
    12  
    13  type dict map[string]any
    14  
    15  func TestValidate(t *testing.T) {
    16  	config := dict{
    17  		"version": "3.0",
    18  		"services": dict{
    19  			"foo": dict{
    20  				"image": "busybox",
    21  			},
    22  		},
    23  	}
    24  
    25  	assert.NilError(t, Validate(config, "3.0"))
    26  	assert.NilError(t, Validate(config, "3"))
    27  	assert.NilError(t, Validate(config, ""))
    28  	assert.ErrorContains(t, Validate(config, "1.0"), "unsupported Compose file version: 1.0")
    29  	assert.ErrorContains(t, Validate(config, "12345"), "unsupported Compose file version: 12345")
    30  }
    31  
    32  func TestValidateUndefinedTopLevelOption(t *testing.T) {
    33  	config := dict{
    34  		"version": "3.0",
    35  		"helicopters": dict{
    36  			"foo": dict{
    37  				"image": "busybox",
    38  			},
    39  		},
    40  	}
    41  
    42  	err := Validate(config, "3.0")
    43  	assert.ErrorContains(t, err, "Additional property helicopters is not allowed")
    44  }
    45  
    46  func TestValidateAllowsXTopLevelFields(t *testing.T) {
    47  	config := dict{
    48  		"version":       "3.4",
    49  		"x-extra-stuff": dict{},
    50  	}
    51  
    52  	err := Validate(config, "3.4")
    53  	assert.NilError(t, err)
    54  }
    55  
    56  func TestValidateAllowsXFields(t *testing.T) {
    57  	config := dict{
    58  		"version": "3.7",
    59  		"services": dict{
    60  			"bar": dict{
    61  				"x-extra-stuff": dict{},
    62  			},
    63  		},
    64  		"volumes": dict{
    65  			"bar": dict{
    66  				"x-extra-stuff": dict{},
    67  			},
    68  		},
    69  		"networks": dict{
    70  			"bar": dict{
    71  				"x-extra-stuff": dict{},
    72  			},
    73  		},
    74  		"configs": dict{
    75  			"bar": dict{
    76  				"x-extra-stuff": dict{},
    77  			},
    78  		},
    79  		"secrets": dict{
    80  			"bar": dict{
    81  				"x-extra-stuff": dict{},
    82  			},
    83  		},
    84  	}
    85  	err := Validate(config, "3.7")
    86  	assert.NilError(t, err)
    87  }
    88  
    89  func TestValidateCredentialSpecs(t *testing.T) {
    90  	tests := []struct {
    91  		version     string
    92  		expectedErr string
    93  	}{
    94  		{version: "3.0", expectedErr: "credential_spec"},
    95  		{version: "3.1", expectedErr: "credential_spec"},
    96  		{version: "3.2", expectedErr: "credential_spec"},
    97  		{version: "3.3", expectedErr: "config"},
    98  		{version: "3.4", expectedErr: "config"},
    99  		{version: "3.5", expectedErr: "config"},
   100  		{version: "3.6", expectedErr: "config"},
   101  		{version: "3.7", expectedErr: "config"},
   102  		{version: "3.8"},
   103  		{version: "3.9"},
   104  		{version: "3.10"},
   105  		{version: "3.11"},
   106  		{version: "3.12"},
   107  		{version: "3"},
   108  		{version: ""},
   109  	}
   110  
   111  	for _, tc := range tests {
   112  		tc := tc
   113  		t.Run(tc.version, func(t *testing.T) {
   114  			config := dict{
   115  				"version": "99.99",
   116  				"services": dict{
   117  					"foo": dict{
   118  						"image": "busybox",
   119  						"credential_spec": dict{
   120  							"config": "foobar",
   121  						},
   122  					},
   123  				},
   124  			}
   125  			err := Validate(config, tc.version)
   126  			if tc.expectedErr != "" {
   127  				assert.ErrorContains(t, err, fmt.Sprintf("Additional property %s is not allowed", tc.expectedErr))
   128  			} else {
   129  				assert.NilError(t, err)
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  func TestValidateSecretConfigNames(t *testing.T) {
   136  	config := dict{
   137  		"version": "3.5",
   138  		"configs": dict{
   139  			"bar": dict{
   140  				"name": "foobar",
   141  			},
   142  		},
   143  		"secrets": dict{
   144  			"baz": dict{
   145  				"name": "foobaz",
   146  			},
   147  		},
   148  	}
   149  
   150  	err := Validate(config, "3.5")
   151  	assert.NilError(t, err)
   152  }
   153  
   154  func TestValidateInvalidVersion(t *testing.T) {
   155  	config := dict{
   156  		"version": "2.1",
   157  		"services": dict{
   158  			"foo": dict{
   159  				"image": "busybox",
   160  			},
   161  		},
   162  	}
   163  
   164  	err := Validate(config, "2.1")
   165  	assert.ErrorContains(t, err, "unsupported Compose file version: 2.1")
   166  }
   167  
   168  type array []any
   169  
   170  func TestValidatePlacement(t *testing.T) {
   171  	config := dict{
   172  		"version": "3.3",
   173  		"services": dict{
   174  			"foo": dict{
   175  				"image": "busybox",
   176  				"deploy": dict{
   177  					"placement": dict{
   178  						"preferences": array{
   179  							dict{
   180  								"spread": "node.labels.az",
   181  							},
   182  						},
   183  					},
   184  				},
   185  			},
   186  		},
   187  	}
   188  
   189  	assert.NilError(t, Validate(config, "3.3"))
   190  }
   191  
   192  func TestValidateIsolation(t *testing.T) {
   193  	config := dict{
   194  		"version": "3.5",
   195  		"services": dict{
   196  			"foo": dict{
   197  				"image":     "busybox",
   198  				"isolation": "some-isolation-value",
   199  			},
   200  		},
   201  	}
   202  	assert.NilError(t, Validate(config, "3.5"))
   203  }
   204  
   205  func TestValidateRollbackConfig(t *testing.T) {
   206  	config := dict{
   207  		"version": "3.4",
   208  		"services": dict{
   209  			"foo": dict{
   210  				"image": "busybox",
   211  				"deploy": dict{
   212  					"rollback_config": dict{
   213  						"parallelism": 1,
   214  					},
   215  				},
   216  			},
   217  		},
   218  	}
   219  
   220  	assert.NilError(t, Validate(config, "3.7"))
   221  }
   222  
   223  func TestValidateRollbackConfigWithOrder(t *testing.T) {
   224  	config := dict{
   225  		"version": "3.4",
   226  		"services": dict{
   227  			"foo": dict{
   228  				"image": "busybox",
   229  				"deploy": dict{
   230  					"rollback_config": dict{
   231  						"parallelism": 1,
   232  						"order":       "start-first",
   233  					},
   234  				},
   235  			},
   236  		},
   237  	}
   238  
   239  	assert.NilError(t, Validate(config, "3.7"))
   240  }
   241  
   242  func TestValidateRollbackConfigWithUpdateConfig(t *testing.T) {
   243  	config := dict{
   244  		"version": "3.4",
   245  		"services": dict{
   246  			"foo": dict{
   247  				"image": "busybox",
   248  				"deploy": dict{
   249  					"update_config": dict{
   250  						"parallelism": 1,
   251  						"order":       "start-first",
   252  					},
   253  					"rollback_config": dict{
   254  						"parallelism": 1,
   255  						"order":       "start-first",
   256  					},
   257  				},
   258  			},
   259  		},
   260  	}
   261  
   262  	assert.NilError(t, Validate(config, "3.7"))
   263  }
   264  
   265  func TestValidateRollbackConfigWithUpdateConfigFull(t *testing.T) {
   266  	config := dict{
   267  		"version": "3.4",
   268  		"services": dict{
   269  			"foo": dict{
   270  				"image": "busybox",
   271  				"deploy": dict{
   272  					"update_config": dict{
   273  						"parallelism":    1,
   274  						"order":          "start-first",
   275  						"delay":          "10s",
   276  						"failure_action": "pause",
   277  						"monitor":        "10s",
   278  					},
   279  					"rollback_config": dict{
   280  						"parallelism":    1,
   281  						"order":          "start-first",
   282  						"delay":          "10s",
   283  						"failure_action": "pause",
   284  						"monitor":        "10s",
   285  					},
   286  				},
   287  			},
   288  		},
   289  	}
   290  
   291  	assert.NilError(t, Validate(config, "3.7"))
   292  }