ac90c6f008
An ACL policy with a block without label generates unexpected results. For example, a policy such as this: ``` namespace { policy = "read" } ``` Is applied to a namespace called `policy` instead of the documented behaviour of applying it to the `default` namespace. This happens because of the way HCL1 decodes blocks. Since it doesn't know if a block is expected to have a label it applies the `key` tag to the content of the block and, in the example above, the first key is `policy`, so it sets that as the `namespace` block label. Since this happens internally in the HCL decoder it's not possible to detect the problem externally. Fixing the problem inside the decoder is challenging because the JSON and HCL parsers generate different ASTs that makes impossible to differentiate between a JSON tree from an invalid HCL tree within the decoder. The fix in this commit consists of manually parsing the policy after decoding to clear labels that were not set in the file. This allows the validation rules to consistently catch and return any errors, no matter if the policy is an invalid HCL or JSON.
901 lines
16 KiB
Go
901 lines
16 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package acl
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestParse(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
type tcase struct {
|
|
Raw string
|
|
ErrStr string
|
|
Expect *Policy
|
|
}
|
|
tcases := []tcase{
|
|
{
|
|
`
|
|
namespace "default" {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
Namespaces: []*NamespacePolicy{
|
|
{
|
|
Name: "default",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListJobs,
|
|
NamespaceCapabilityParseJob,
|
|
NamespaceCapabilityReadJob,
|
|
NamespaceCapabilityCSIListVolume,
|
|
NamespaceCapabilityCSIReadVolume,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
namespace "default" {
|
|
policy = "read"
|
|
}
|
|
namespace "other" {
|
|
policy = "write"
|
|
}
|
|
namespace "secret" {
|
|
capabilities = ["deny", "read-logs"]
|
|
}
|
|
namespace "apps" {
|
|
variables {
|
|
path "jobs/write-does-not-imply-read-or-delete" {
|
|
capabilities = ["write"]
|
|
}
|
|
path "project/read-implies-list" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "project/explicit" {
|
|
capabilities = ["read", "list", "destroy"]
|
|
}
|
|
}
|
|
}
|
|
namespace "autoscaler" {
|
|
policy = "scale"
|
|
}
|
|
host_volume "production-tls-*" {
|
|
capabilities = ["mount-readonly"]
|
|
}
|
|
host_volume "staging-tls-*" {
|
|
policy = "write"
|
|
}
|
|
node_pool "prod" {
|
|
capabilities = ["read"]
|
|
}
|
|
node_pool "dev" {
|
|
policy = "write"
|
|
}
|
|
agent {
|
|
policy = "read"
|
|
}
|
|
node {
|
|
policy = "write"
|
|
}
|
|
operator {
|
|
policy = "deny"
|
|
}
|
|
quota {
|
|
policy = "read"
|
|
}
|
|
plugin {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
Namespaces: []*NamespacePolicy{
|
|
{
|
|
Name: "default",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListJobs,
|
|
NamespaceCapabilityParseJob,
|
|
NamespaceCapabilityReadJob,
|
|
NamespaceCapabilityCSIListVolume,
|
|
NamespaceCapabilityCSIReadVolume,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
},
|
|
},
|
|
{
|
|
Name: "other",
|
|
Policy: PolicyWrite,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListJobs,
|
|
NamespaceCapabilityParseJob,
|
|
NamespaceCapabilityReadJob,
|
|
NamespaceCapabilityCSIListVolume,
|
|
NamespaceCapabilityCSIReadVolume,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
NamespaceCapabilityScaleJob,
|
|
NamespaceCapabilitySubmitJob,
|
|
NamespaceCapabilityDispatchJob,
|
|
NamespaceCapabilityReadLogs,
|
|
NamespaceCapabilityReadFS,
|
|
NamespaceCapabilityAllocExec,
|
|
NamespaceCapabilityAllocLifecycle,
|
|
NamespaceCapabilityCSIMountVolume,
|
|
NamespaceCapabilityCSIWriteVolume,
|
|
NamespaceCapabilitySubmitRecommendation,
|
|
},
|
|
},
|
|
{
|
|
Name: "secret",
|
|
Capabilities: []string{
|
|
NamespaceCapabilityDeny,
|
|
NamespaceCapabilityReadLogs,
|
|
},
|
|
},
|
|
{
|
|
Name: "apps",
|
|
Variables: &VariablesPolicy{
|
|
Paths: []*VariablesPathPolicy{
|
|
{
|
|
PathSpec: "jobs/write-does-not-imply-read-or-delete",
|
|
Capabilities: []string{VariablesCapabilityWrite},
|
|
},
|
|
{
|
|
PathSpec: "project/read-implies-list",
|
|
Capabilities: []string{
|
|
VariablesCapabilityRead,
|
|
VariablesCapabilityList,
|
|
},
|
|
},
|
|
{
|
|
PathSpec: "project/explicit",
|
|
Capabilities: []string{
|
|
VariablesCapabilityRead,
|
|
VariablesCapabilityList,
|
|
VariablesCapabilityDestroy,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "autoscaler",
|
|
Policy: PolicyScale,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityScaleJob,
|
|
},
|
|
},
|
|
},
|
|
HostVolumes: []*HostVolumePolicy{
|
|
{
|
|
Name: "production-tls-*",
|
|
Capabilities: []string{"mount-readonly"},
|
|
},
|
|
{
|
|
Name: "staging-tls-*",
|
|
Policy: "write",
|
|
Capabilities: []string{
|
|
"mount-readonly",
|
|
"mount-readwrite",
|
|
},
|
|
},
|
|
},
|
|
NodePools: []*NodePoolPolicy{
|
|
{
|
|
Name: "prod",
|
|
Capabilities: []string{"read"},
|
|
},
|
|
{
|
|
Name: "dev",
|
|
Policy: "write",
|
|
Capabilities: []string{"delete", "read", "write"},
|
|
},
|
|
},
|
|
Agent: &AgentPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
Node: &NodePolicy{
|
|
Policy: PolicyWrite,
|
|
},
|
|
Operator: &OperatorPolicy{
|
|
Policy: PolicyDeny,
|
|
},
|
|
Quota: &QuotaPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
Plugin: &PluginPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"namespace": [
|
|
{
|
|
"default": {
|
|
"policy": "read"
|
|
},
|
|
},
|
|
{
|
|
"other": {
|
|
"policy": "write"
|
|
},
|
|
},
|
|
{
|
|
"secret": {
|
|
"capabilities": [
|
|
"deny",
|
|
"read-logs"
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"apps": {
|
|
"variables": [
|
|
{
|
|
"path": [
|
|
{
|
|
"jobs/write-does-not-imply-read-or-delete": {
|
|
"capabilities": ["write"],
|
|
},
|
|
},
|
|
{
|
|
"project/read-implies-list": {
|
|
"capabilities": ["read"],
|
|
},
|
|
},
|
|
{
|
|
"project/explicit": {
|
|
"capabilities": ["read", "list", "destroy"],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
"autoscaler": {
|
|
"policy": "scale"
|
|
},
|
|
},
|
|
],
|
|
"host_volume": [
|
|
{
|
|
"production-tls-*": {
|
|
"capabilities": ["mount-readonly"]
|
|
}
|
|
},
|
|
{
|
|
"staging-tls-*": {
|
|
"policy": "write"
|
|
}
|
|
}
|
|
],
|
|
"node_pool": [
|
|
{
|
|
"prod": {
|
|
"capabilities": ["read"]
|
|
}
|
|
},
|
|
{
|
|
"dev": {
|
|
"policy": "write"
|
|
}
|
|
}
|
|
],
|
|
"agent": {
|
|
"policy": "read"
|
|
},
|
|
"node": {
|
|
"policy": "write"
|
|
},
|
|
"operator": {
|
|
"policy": "deny"
|
|
},
|
|
"quota": {
|
|
"policy": "read"
|
|
},
|
|
"plugin": {
|
|
"policy": "read"
|
|
}
|
|
}`,
|
|
"",
|
|
&Policy{
|
|
Namespaces: []*NamespacePolicy{
|
|
{
|
|
Name: "default",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListJobs,
|
|
NamespaceCapabilityParseJob,
|
|
NamespaceCapabilityReadJob,
|
|
NamespaceCapabilityCSIListVolume,
|
|
NamespaceCapabilityCSIReadVolume,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
},
|
|
},
|
|
{
|
|
Name: "other",
|
|
Policy: PolicyWrite,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListJobs,
|
|
NamespaceCapabilityParseJob,
|
|
NamespaceCapabilityReadJob,
|
|
NamespaceCapabilityCSIListVolume,
|
|
NamespaceCapabilityCSIReadVolume,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
NamespaceCapabilityScaleJob,
|
|
NamespaceCapabilitySubmitJob,
|
|
NamespaceCapabilityDispatchJob,
|
|
NamespaceCapabilityReadLogs,
|
|
NamespaceCapabilityReadFS,
|
|
NamespaceCapabilityAllocExec,
|
|
NamespaceCapabilityAllocLifecycle,
|
|
NamespaceCapabilityCSIMountVolume,
|
|
NamespaceCapabilityCSIWriteVolume,
|
|
NamespaceCapabilitySubmitRecommendation,
|
|
},
|
|
},
|
|
{
|
|
Name: "secret",
|
|
Capabilities: []string{
|
|
NamespaceCapabilityDeny,
|
|
NamespaceCapabilityReadLogs,
|
|
},
|
|
},
|
|
{
|
|
Name: "apps",
|
|
Variables: &VariablesPolicy{
|
|
Paths: []*VariablesPathPolicy{
|
|
{
|
|
PathSpec: "jobs/write-does-not-imply-read-or-delete",
|
|
Capabilities: []string{VariablesCapabilityWrite},
|
|
},
|
|
{
|
|
PathSpec: "project/read-implies-list",
|
|
Capabilities: []string{
|
|
VariablesCapabilityRead,
|
|
VariablesCapabilityList,
|
|
},
|
|
},
|
|
{
|
|
PathSpec: "project/explicit",
|
|
Capabilities: []string{
|
|
VariablesCapabilityRead,
|
|
VariablesCapabilityList,
|
|
VariablesCapabilityDestroy,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Name: "autoscaler",
|
|
Policy: PolicyScale,
|
|
Capabilities: []string{
|
|
NamespaceCapabilityListScalingPolicies,
|
|
NamespaceCapabilityReadScalingPolicy,
|
|
NamespaceCapabilityReadJobScaling,
|
|
NamespaceCapabilityScaleJob,
|
|
},
|
|
},
|
|
},
|
|
HostVolumes: []*HostVolumePolicy{
|
|
{
|
|
Name: "production-tls-*",
|
|
Capabilities: []string{"mount-readonly"},
|
|
},
|
|
{
|
|
Name: "staging-tls-*",
|
|
Policy: "write",
|
|
Capabilities: []string{
|
|
"mount-readonly",
|
|
"mount-readwrite",
|
|
},
|
|
},
|
|
},
|
|
NodePools: []*NodePoolPolicy{
|
|
{
|
|
Name: "prod",
|
|
Capabilities: []string{"read"},
|
|
},
|
|
{
|
|
Name: "dev",
|
|
Policy: "write",
|
|
Capabilities: []string{"delete", "read", "write"},
|
|
},
|
|
},
|
|
Agent: &AgentPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
Node: &NodePolicy{
|
|
Policy: PolicyWrite,
|
|
},
|
|
Operator: &OperatorPolicy{
|
|
Policy: PolicyDeny,
|
|
},
|
|
Quota: &QuotaPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
Plugin: &PluginPolicy{
|
|
Policy: PolicyRead,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
namespace "default" {
|
|
policy = "foo"
|
|
}
|
|
`,
|
|
"Invalid namespace policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"Invalid namespace name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"namespace": [
|
|
{
|
|
"": {
|
|
"policy": "read"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
"Invalid namespace name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace "dev" {
|
|
variables "*" {
|
|
capabilities = ["read", "write"]
|
|
}
|
|
}
|
|
`,
|
|
"Invalid variable policy: no variable paths in namespace dev",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace "dev" {
|
|
policy = "read"
|
|
|
|
variables {
|
|
path {}
|
|
path "nomad/jobs/example" {
|
|
capabilities = ["read"]
|
|
}
|
|
}
|
|
}
|
|
`,
|
|
"Invalid missing variable path in namespace",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"namespace": [
|
|
{
|
|
"dev": {
|
|
"policy": "read",
|
|
"variables": [
|
|
{
|
|
"paths": [
|
|
{
|
|
"": {
|
|
"capabilities": ["read"]
|
|
}
|
|
}
|
|
]
|
|
]
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
"no variable paths in namespace dev",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace "default" {
|
|
capabilities = ["deny", "foo"]
|
|
}
|
|
`,
|
|
"Invalid namespace capability",
|
|
nil,
|
|
},
|
|
{
|
|
`namespace {}`,
|
|
"invalid acl policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
agent {
|
|
policy = "foo"
|
|
}
|
|
`,
|
|
"Invalid agent policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
node {
|
|
policy = "foo"
|
|
}
|
|
`,
|
|
"Invalid node policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
operator {
|
|
policy = "foo"
|
|
}
|
|
`,
|
|
"Invalid operator policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
quota {
|
|
policy = "foo"
|
|
}
|
|
`,
|
|
"Invalid quota policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"Name": "my-policy",
|
|
"Description": "This is a great policy",
|
|
"Rules": "anything"
|
|
}
|
|
`,
|
|
"Invalid policy",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace "has a space"{
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"Invalid namespace name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
namespace "default" {
|
|
capabilities = ["sentinel-override"]
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
Namespaces: []*NamespacePolicy{
|
|
{
|
|
Name: "default",
|
|
Policy: "",
|
|
Capabilities: []string{
|
|
NamespaceCapabilitySentinelOverride,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
node_pool "pool-read-only" {
|
|
policy = "read"
|
|
}
|
|
|
|
node_pool "pool-read-write" {
|
|
policy = "write"
|
|
}
|
|
|
|
node_pool "pool-read-upsert" {
|
|
policy = "read"
|
|
capabilities = ["write"]
|
|
}
|
|
|
|
node_pool "pool-multiple-capabilities" {
|
|
policy = "read"
|
|
capabilities = ["write", "delete"]
|
|
}
|
|
|
|
node_pool "pool-deny-policy" {
|
|
policy = "deny"
|
|
capabilities = ["write"]
|
|
}
|
|
|
|
node_pool "pool-deny-capability" {
|
|
capabilities = ["deny", "read"]
|
|
}
|
|
|
|
node_pool "pool-*" {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
NodePools: []*NodePoolPolicy{
|
|
{
|
|
Name: "pool-read-only",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityRead,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-read-write",
|
|
Policy: PolicyWrite,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityDelete,
|
|
NodePoolCapabilityRead,
|
|
NodePoolCapabilityWrite,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-read-upsert",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityWrite,
|
|
NodePoolCapabilityRead,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-multiple-capabilities",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityWrite,
|
|
NodePoolCapabilityDelete,
|
|
NodePoolCapabilityRead,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-deny-policy",
|
|
Policy: PolicyDeny,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityWrite,
|
|
NodePoolCapabilityDeny,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-deny-capability",
|
|
Policy: "",
|
|
Capabilities: []string{
|
|
NodePoolCapabilityDeny,
|
|
NodePoolCapabilityRead,
|
|
},
|
|
},
|
|
{
|
|
Name: "pool-*",
|
|
Policy: PolicyRead,
|
|
Capabilities: []string{
|
|
NodePoolCapabilityRead,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
node_pool "" {
|
|
}
|
|
`,
|
|
"Invalid node pool name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
node_pool "pool%" {
|
|
}
|
|
`,
|
|
"Invalid node pool name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
node_pool "my-pool" {
|
|
capabilities = ["read", "invalid"]
|
|
}
|
|
`,
|
|
"Invalid node pool capability",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
node_pool {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"Invalid node pool name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"node_pool": [
|
|
{
|
|
"": {
|
|
"policy": "read"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
"Invalid node pool name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
host_volume "production-tls-*" {
|
|
capabilities = ["mount-readonly"]
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
HostVolumes: []*HostVolumePolicy{
|
|
{
|
|
Name: "production-tls-*",
|
|
Policy: "",
|
|
Capabilities: []string{
|
|
HostVolumeCapabilityMountReadOnly,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
host_volume "production-tls-*" {
|
|
capabilities = ["mount-readwrite"]
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
HostVolumes: []*HostVolumePolicy{
|
|
{
|
|
Name: "production-tls-*",
|
|
Policy: "",
|
|
Capabilities: []string{
|
|
HostVolumeCapabilityMountReadWrite,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
host_volume "volume has a space" {
|
|
capabilities = ["mount-readwrite"]
|
|
}
|
|
`,
|
|
"Invalid host volume name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
host_volume {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
"Invalid host volume name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
{
|
|
"host_volume": [
|
|
{
|
|
"": {
|
|
"policy": "read"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
"Invalid host volume name",
|
|
nil,
|
|
},
|
|
{
|
|
`
|
|
plugin {
|
|
policy = "list"
|
|
}
|
|
`,
|
|
"",
|
|
&Policy{
|
|
Plugin: &PluginPolicy{
|
|
Policy: PolicyList,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
`
|
|
plugin {
|
|
policy = "reader"
|
|
}
|
|
`,
|
|
"Invalid plugin policy",
|
|
nil,
|
|
},
|
|
}
|
|
|
|
for idx, tc := range tcases {
|
|
t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
|
|
p, err := Parse(tc.Raw)
|
|
if err != nil {
|
|
if tc.ErrStr == "" {
|
|
t.Fatalf("Unexpected err: %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), tc.ErrStr) {
|
|
t.Fatalf("Unexpected err: %v", err)
|
|
}
|
|
return
|
|
}
|
|
if err == nil && tc.ErrStr != "" {
|
|
t.Fatalf("Missing expected err")
|
|
}
|
|
tc.Expect.Raw = tc.Raw
|
|
assert.EqualValues(t, tc.Expect, p)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParse_BadInput(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
inputs := []string{
|
|
`namespace "\500" {}`,
|
|
}
|
|
|
|
for i, c := range inputs {
|
|
t.Run(fmt.Sprintf("%d: %v", i, c), func(t *testing.T) {
|
|
_, err := Parse(c)
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
}
|