github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/cli/compose/schema/schema_test.go (about)

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