github.com/nats-io/jwt/v2@v2.5.6/v1compat/exports_test.go (about)

     1  /*
     2   * Copyright 2018 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package jwt
    17  
    18  import (
    19  	"sort"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func TestSimpleExportValidation(t *testing.T) {
    25  	e := &Export{Subject: "foo", Type: Stream}
    26  
    27  	vr := CreateValidationResults()
    28  	e.Validate(vr)
    29  
    30  	if !vr.IsEmpty() {
    31  		t.Errorf("simple export should validate cleanly")
    32  	}
    33  
    34  	e.Type = Service
    35  	vr = CreateValidationResults()
    36  	e.Validate(vr)
    37  
    38  	if !vr.IsEmpty() {
    39  		t.Errorf("simple export should validate cleanly")
    40  	}
    41  }
    42  
    43  func TestResponseTypeValidation(t *testing.T) {
    44  	e := &Export{Subject: "foo", Type: Stream, ResponseType: ResponseTypeSingleton}
    45  
    46  	vr := CreateValidationResults()
    47  	e.Validate(vr)
    48  
    49  	if vr.IsEmpty() {
    50  		t.Errorf("response type on stream should have an validation issue")
    51  	}
    52  	if e.IsSingleResponse() {
    53  		t.Errorf("response type should always fail for stream")
    54  	}
    55  
    56  	e.Type = Service
    57  	vr = CreateValidationResults()
    58  	e.Validate(vr)
    59  	if !vr.IsEmpty() {
    60  		t.Errorf("response type on service should validate cleanly")
    61  	}
    62  	if !e.IsSingleResponse() || e.IsChunkedResponse() || e.IsStreamResponse() {
    63  		t.Errorf("response type should be single")
    64  	}
    65  
    66  	e.ResponseType = ResponseTypeChunked
    67  	vr = CreateValidationResults()
    68  	e.Validate(vr)
    69  	if !vr.IsEmpty() {
    70  		t.Errorf("response type on service should validate cleanly")
    71  	}
    72  	if e.IsSingleResponse() || !e.IsChunkedResponse() || e.IsStreamResponse() {
    73  		t.Errorf("response type should be chunk")
    74  	}
    75  
    76  	e.ResponseType = ResponseTypeStream
    77  	vr = CreateValidationResults()
    78  	e.Validate(vr)
    79  	if !vr.IsEmpty() {
    80  		t.Errorf("response type on service should validate cleanly")
    81  	}
    82  	if e.IsSingleResponse() || e.IsChunkedResponse() || !e.IsStreamResponse() {
    83  		t.Errorf("response type should be stream")
    84  	}
    85  
    86  	e.ResponseType = ""
    87  	vr = CreateValidationResults()
    88  	e.Validate(vr)
    89  	if !vr.IsEmpty() {
    90  		t.Errorf("response type on service should validate cleanly")
    91  	}
    92  	if !e.IsSingleResponse() || e.IsChunkedResponse() || e.IsStreamResponse() {
    93  		t.Errorf("response type should be single")
    94  	}
    95  
    96  	e.ResponseType = "bad"
    97  	vr = CreateValidationResults()
    98  	e.Validate(vr)
    99  	if vr.IsEmpty() {
   100  		t.Errorf("response type should match available options")
   101  	}
   102  	if e.IsSingleResponse() || e.IsChunkedResponse() || e.IsStreamResponse() {
   103  		t.Errorf("response type should be bad")
   104  	}
   105  }
   106  
   107  func TestInvalidExportType(t *testing.T) {
   108  	i := &Export{Subject: "foo", Type: Unknown}
   109  
   110  	vr := CreateValidationResults()
   111  	i.Validate(vr)
   112  
   113  	if vr.IsEmpty() {
   114  		t.Errorf("export with bad type should not validate cleanly")
   115  	}
   116  
   117  	if !vr.IsBlocking(true) {
   118  		t.Errorf("invalid type is blocking")
   119  	}
   120  }
   121  
   122  func TestOverlappingExports(t *testing.T) {
   123  	i := &Export{Subject: "bar.foo", Type: Stream}
   124  	i2 := &Export{Subject: "bar.*", Type: Stream}
   125  
   126  	exports := &Exports{}
   127  	exports.Add(i, i2)
   128  
   129  	vr := CreateValidationResults()
   130  	exports.Validate(vr)
   131  
   132  	if len(vr.Issues) != 1 {
   133  		t.Errorf("export has overlapping subjects")
   134  	}
   135  }
   136  
   137  func TestDifferentExportTypes_OverlapOK(t *testing.T) {
   138  	i := &Export{Subject: "bar.foo", Type: Service}
   139  	i2 := &Export{Subject: "bar.*", Type: Stream}
   140  
   141  	exports := &Exports{}
   142  	exports.Add(i, i2)
   143  
   144  	vr := CreateValidationResults()
   145  	exports.Validate(vr)
   146  
   147  	if len(vr.Issues) != 0 {
   148  		t.Errorf("should allow overlaps on different export kind")
   149  	}
   150  }
   151  
   152  func TestDifferentExportTypes_SameSubjectOK(t *testing.T) {
   153  	i := &Export{Subject: "bar", Type: Service}
   154  	i2 := &Export{Subject: "bar", Type: Stream}
   155  
   156  	exports := &Exports{}
   157  	exports.Add(i, i2)
   158  
   159  	vr := CreateValidationResults()
   160  	exports.Validate(vr)
   161  
   162  	if len(vr.Issues) != 0 {
   163  		t.Errorf("should allow overlaps on different export kind")
   164  	}
   165  }
   166  
   167  func TestSameExportType_SameSubject(t *testing.T) {
   168  	i := &Export{Subject: "bar", Type: Service}
   169  	i2 := &Export{Subject: "bar", Type: Service}
   170  
   171  	exports := &Exports{}
   172  	exports.Add(i, i2)
   173  
   174  	vr := CreateValidationResults()
   175  	exports.Validate(vr)
   176  
   177  	if len(vr.Issues) != 1 {
   178  		t.Errorf("should not allow same subject on same export kind")
   179  	}
   180  }
   181  
   182  func TestExportRevocation(t *testing.T) {
   183  	akp := createAccountNKey(t)
   184  	apk := publicKey(akp, t)
   185  	account := NewAccountClaims(apk)
   186  	e := &Export{Subject: "foo", Type: Stream}
   187  
   188  	account.Exports.Add(e)
   189  
   190  	pubKey := "bar"
   191  	now := time.Now()
   192  
   193  	// test that clear is safe before we add any
   194  	e.ClearRevocation(pubKey)
   195  
   196  	if e.IsRevokedAt(pubKey, now) {
   197  		t.Errorf("no revocation was added so is revoked should be false")
   198  	}
   199  
   200  	e.RevokeAt(pubKey, now.Add(time.Second*100))
   201  
   202  	if !e.IsRevokedAt(pubKey, now) {
   203  		t.Errorf("revocation should hold when timestamp is in the future")
   204  	}
   205  
   206  	if e.IsRevokedAt(pubKey, now.Add(time.Second*150)) {
   207  		t.Errorf("revocation should time out")
   208  	}
   209  
   210  	e.RevokeAt(pubKey, now.Add(time.Second*50)) // shouldn't change the revocation, you can't move it in
   211  
   212  	if !e.IsRevokedAt(pubKey, now.Add(time.Second*60)) {
   213  		t.Errorf("revocation should hold, 100 > 50")
   214  	}
   215  
   216  	encoded, _ := account.Encode(akp)
   217  	decoded, _ := DecodeAccountClaims(encoded)
   218  
   219  	if !decoded.Exports[0].IsRevokedAt(pubKey, now.Add(time.Second*60)) {
   220  		t.Errorf("revocation should last across encoding")
   221  	}
   222  
   223  	e.ClearRevocation(pubKey)
   224  
   225  	if e.IsRevokedAt(pubKey, now) {
   226  		t.Errorf("revocations should be cleared")
   227  	}
   228  
   229  	e.RevokeAt(pubKey, now.Add(time.Second*1000))
   230  
   231  	if !e.IsRevoked(pubKey) {
   232  		t.Errorf("revocation be true we revoked in the future")
   233  	}
   234  }
   235  
   236  func TestExportTrackLatency(t *testing.T) {
   237  	e := &Export{Subject: "foo", Type: Service}
   238  	e.Latency = &ServiceLatency{Sampling: 100, Results: "results"}
   239  	vr := CreateValidationResults()
   240  	e.Validate(vr)
   241  	if !vr.IsEmpty() {
   242  		t.Errorf("Expected to validate with simple tracking")
   243  	}
   244  
   245  	e = &Export{Subject: "foo", Type: Stream}
   246  	e.Latency = &ServiceLatency{Sampling: 100, Results: "results"}
   247  	vr = CreateValidationResults()
   248  	e.Validate(vr)
   249  	if vr.IsEmpty() {
   250  		t.Errorf("adding latency tracking to a stream should have an validation issue")
   251  	}
   252  
   253  	e = &Export{Subject: "foo", Type: Service}
   254  	e.Latency = &ServiceLatency{Sampling: 0, Results: "results"}
   255  	vr = CreateValidationResults()
   256  	e.Validate(vr)
   257  	if vr.IsEmpty() {
   258  		t.Errorf("Sampling <1 should have a validation issue")
   259  	}
   260  
   261  	e = &Export{Subject: "foo", Type: Service}
   262  	e.Latency = &ServiceLatency{Sampling: 122, Results: "results"}
   263  	vr = CreateValidationResults()
   264  	e.Validate(vr)
   265  	if vr.IsEmpty() {
   266  		t.Errorf("Sampling >100 should have a validation issue")
   267  	}
   268  
   269  	e = &Export{Subject: "foo", Type: Service}
   270  	e.Latency = &ServiceLatency{Sampling: 22, Results: "results.*"}
   271  	vr = CreateValidationResults()
   272  	e.Validate(vr)
   273  	if vr.IsEmpty() {
   274  		t.Errorf("Results subject needs to be valid publish subject")
   275  	}
   276  }
   277  
   278  func TestExport_Sorting(t *testing.T) {
   279  	var exports Exports
   280  	exports.Add(&Export{Subject: "x", Type: Service})
   281  	exports.Add(&Export{Subject: "z", Type: Service})
   282  	exports.Add(&Export{Subject: "y", Type: Service})
   283  	if exports[0].Subject != "x" {
   284  		t.Fatal("added export not in expected order")
   285  	}
   286  	sort.Sort(exports)
   287  	if exports[0].Subject != "x" && exports[1].Subject != "y" && exports[2].Subject != "z" {
   288  		t.Fatal("exports not sorted")
   289  	}
   290  }