github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/tests/unit/abilities/variable-test.js (about)

     1  /* eslint-disable ember/avoid-leaking-state-in-ember-objects */
     2  import { module, test } from 'qunit';
     3  import { setupTest } from 'ember-qunit';
     4  import Service from '@ember/service';
     5  import setupAbility from 'nomad-ui/tests/helpers/setup-ability';
     6  
     7  module('Unit | Ability | variable', function (hooks) {
     8    setupTest(hooks);
     9    setupAbility('variable')(hooks);
    10    hooks.beforeEach(function () {
    11      const mockSystem = Service.extend({
    12        features: [],
    13      });
    14  
    15      this.owner.register('service:system', mockSystem);
    16    });
    17  
    18    module('#list', function () {
    19      test('it does not permit listing variables by default', function (assert) {
    20        const mockToken = Service.extend({
    21          aclEnabled: true,
    22        });
    23  
    24        this.owner.register('service:token', mockToken);
    25  
    26        assert.notOk(this.ability.canList);
    27      });
    28  
    29      test('it does not permit listing variables when token type is client', function (assert) {
    30        const mockToken = Service.extend({
    31          aclEnabled: true,
    32          selfToken: { type: 'client' },
    33        });
    34  
    35        this.owner.register('service:token', mockToken);
    36  
    37        assert.notOk(this.ability.canList);
    38      });
    39  
    40      test('it permits listing variables when token type is management', function (assert) {
    41        const mockToken = Service.extend({
    42          aclEnabled: true,
    43          selfToken: { type: 'management' },
    44        });
    45  
    46        this.owner.register('service:token', mockToken);
    47  
    48        assert.ok(this.ability.canList);
    49      });
    50  
    51      test('it permits listing variables when token has Variables with list capabilities in its rules', function (assert) {
    52        const mockToken = Service.extend({
    53          aclEnabled: true,
    54          selfToken: { type: 'client' },
    55          selfTokenPolicies: [
    56            {
    57              rulesJSON: {
    58                Namespaces: [
    59                  {
    60                    Name: 'default',
    61                    Capabilities: [],
    62                    Variables: {
    63                      Paths: [{ Capabilities: ['list'], PathSpec: '*' }],
    64                    },
    65                  },
    66                ],
    67              },
    68            },
    69          ],
    70        });
    71  
    72        this.owner.register('service:token', mockToken);
    73  
    74        assert.ok(this.ability.canList);
    75      });
    76  
    77      test('it does not permit listing variables when token has Variables alone in its rules', function (assert) {
    78        const mockToken = Service.extend({
    79          aclEnabled: true,
    80          selfToken: { type: 'client' },
    81          selfTokenPolicies: [
    82            {
    83              rulesJSON: {
    84                Namespaces: [
    85                  {
    86                    Name: 'default',
    87                    Capabilities: [],
    88                    Variables: {},
    89                  },
    90                ],
    91              },
    92            },
    93          ],
    94        });
    95  
    96        this.owner.register('service:token', mockToken);
    97  
    98        assert.notOk(this.ability.canList);
    99      });
   100  
   101      test('it does not permit listing variables when token has a null Variables block', function (assert) {
   102        const mockToken = Service.extend({
   103          aclEnabled: true,
   104          selfToken: { type: 'client' },
   105          selfTokenPolicies: [
   106            {
   107              rulesJSON: {
   108                Namespaces: [
   109                  {
   110                    Name: 'default',
   111                    Capabilities: [],
   112                    Variables: null,
   113                  },
   114                ],
   115              },
   116            },
   117          ],
   118        });
   119  
   120        this.owner.register('service:token', mockToken);
   121  
   122        assert.notOk(this.ability.canList);
   123      });
   124  
   125      test('it does not permit listing variables when token has a Variables block where paths are without capabilities', function (assert) {
   126        const mockToken = Service.extend({
   127          aclEnabled: true,
   128          selfToken: { type: 'client' },
   129          selfTokenPolicies: [
   130            {
   131              rulesJSON: {
   132                Namespaces: [
   133                  {
   134                    Name: 'default',
   135                    Capabilities: [],
   136                    Variables: {
   137                      Paths: [
   138                        { Capabilities: [], PathSpec: '*' },
   139                        { Capabilities: [], PathSpec: 'foo' },
   140                        { Capabilities: [], PathSpec: 'foo/bar' },
   141                      ],
   142                    },
   143                  },
   144                ],
   145              },
   146            },
   147          ],
   148        });
   149  
   150        this.owner.register('service:token', mockToken);
   151  
   152        assert.notOk(this.ability.canList);
   153      });
   154  
   155      test('it does not permit listing variables when token has no Variables block', function (assert) {
   156        const mockToken = Service.extend({
   157          aclEnabled: true,
   158          selfToken: { type: 'client' },
   159          selfTokenPolicies: [
   160            {
   161              rulesJSON: {
   162                Namespaces: [
   163                  {
   164                    Name: 'default',
   165                    Capabilities: [],
   166                  },
   167                ],
   168              },
   169            },
   170          ],
   171        });
   172  
   173        this.owner.register('service:token', mockToken);
   174  
   175        assert.notOk(this.ability.canList);
   176      });
   177  
   178      test('it permits listing variables when token multiple namespaces, only one of which having a Variables block', function (assert) {
   179        const mockToken = Service.extend({
   180          aclEnabled: true,
   181          selfToken: { type: 'client' },
   182          selfTokenPolicies: [
   183            {
   184              rulesJSON: {
   185                Namespaces: [
   186                  {
   187                    Name: 'default',
   188                    Capabilities: [],
   189                    Variables: null,
   190                  },
   191                  {
   192                    Name: 'nonsense',
   193                    Capabilities: [],
   194                    Variables: {
   195                      Paths: [{ Capabilities: [], PathSpec: '*' }],
   196                    },
   197                  },
   198                  {
   199                    Name: 'shenanigans',
   200                    Capabilities: [],
   201                    Variables: {
   202                      Paths: [
   203                        { Capabilities: ['list'], PathSpec: 'foo/bar/baz' },
   204                      ],
   205                    },
   206                  },
   207                ],
   208              },
   209            },
   210          ],
   211        });
   212  
   213        this.owner.register('service:token', mockToken);
   214  
   215        assert.ok(this.ability.canList);
   216      });
   217    });
   218  
   219    module('#create', function () {
   220      test('it does not permit creating variables by default', function (assert) {
   221        const mockToken = Service.extend({
   222          aclEnabled: true,
   223        });
   224  
   225        this.owner.register('service:token', mockToken);
   226  
   227        assert.notOk(this.ability.canWrite);
   228      });
   229  
   230      test('it permits creating variables when token type is management', function (assert) {
   231        const mockToken = Service.extend({
   232          aclEnabled: true,
   233          selfToken: { type: 'management' },
   234        });
   235  
   236        this.owner.register('service:token', mockToken);
   237  
   238        assert.ok(this.ability.canWrite);
   239      });
   240  
   241      test('it permits creating variables when acl is disabled', function (assert) {
   242        const mockToken = Service.extend({
   243          aclEnabled: false,
   244          selfToken: { type: 'client' },
   245        });
   246  
   247        this.owner.register('service:token', mockToken);
   248  
   249        assert.ok(this.ability.canWrite);
   250      });
   251  
   252      test('it permits creating variables when token has Variables with write capabilities in its rules', function (assert) {
   253        const mockToken = Service.extend({
   254          aclEnabled: true,
   255          selfToken: { type: 'client' },
   256          selfTokenPolicies: [
   257            {
   258              rulesJSON: {
   259                Namespaces: [
   260                  {
   261                    Name: 'default',
   262                    Capabilities: [],
   263                    Variables: {
   264                      Paths: [{ Capabilities: ['write'], PathSpec: '*' }],
   265                    },
   266                  },
   267                ],
   268              },
   269            },
   270          ],
   271        });
   272  
   273        this.owner.register('service:token', mockToken);
   274  
   275        assert.ok(this.ability.canWrite);
   276      });
   277  
   278      test('it handles namespace matching', function (assert) {
   279        const mockToken = Service.extend({
   280          aclEnabled: true,
   281          selfToken: { type: 'client' },
   282          selfTokenPolicies: [
   283            {
   284              rulesJSON: {
   285                Namespaces: [
   286                  {
   287                    Name: 'default',
   288                    Capabilities: [],
   289                    Variables: {
   290                      Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
   291                    },
   292                  },
   293                  {
   294                    Name: 'pablo',
   295                    Capabilities: [],
   296                    Variables: {
   297                      Paths: [{ Capabilities: ['write'], PathSpec: 'foo/bar' }],
   298                    },
   299                  },
   300                ],
   301              },
   302            },
   303          ],
   304        });
   305  
   306        this.owner.register('service:token', mockToken);
   307        this.ability.path = 'foo/bar';
   308        this.ability.namespace = 'pablo';
   309  
   310        assert.ok(this.ability.canWrite);
   311      });
   312    });
   313  
   314    module('#destroy', function () {
   315      test('it does not permit destroying variables by default', function (assert) {
   316        const mockToken = Service.extend({
   317          aclEnabled: true,
   318        });
   319  
   320        this.owner.register('service:token', mockToken);
   321  
   322        assert.notOk(this.ability.canDestroy);
   323      });
   324  
   325      test('it permits destroying variables when token type is management', function (assert) {
   326        const mockToken = Service.extend({
   327          aclEnabled: true,
   328          selfToken: { type: 'management' },
   329        });
   330  
   331        this.owner.register('service:token', mockToken);
   332  
   333        assert.ok(this.ability.canDestroy);
   334      });
   335  
   336      test('it permits destroying variables when acl is disabled', function (assert) {
   337        const mockToken = Service.extend({
   338          aclEnabled: false,
   339          selfToken: { type: 'client' },
   340        });
   341  
   342        this.owner.register('service:token', mockToken);
   343  
   344        assert.ok(this.ability.canDestroy);
   345      });
   346  
   347      test('it permits destroying variables when token has Variables with write capabilities in its rules', function (assert) {
   348        const mockToken = Service.extend({
   349          aclEnabled: true,
   350          selfToken: { type: 'client' },
   351          selfTokenPolicies: [
   352            {
   353              rulesJSON: {
   354                Namespaces: [
   355                  {
   356                    Name: 'default',
   357                    Capabilities: [],
   358                    Variables: {
   359                      Paths: [{ Capabilities: ['destroy'], PathSpec: '*' }],
   360                    },
   361                  },
   362                ],
   363              },
   364            },
   365          ],
   366        });
   367  
   368        this.owner.register('service:token', mockToken);
   369  
   370        assert.ok(this.ability.canDestroy);
   371      });
   372  
   373      test('it handles namespace matching', function (assert) {
   374        const mockToken = Service.extend({
   375          aclEnabled: true,
   376          selfToken: { type: 'client' },
   377          selfTokenPolicies: [
   378            {
   379              rulesJSON: {
   380                Namespaces: [
   381                  {
   382                    Name: 'default',
   383                    Capabilities: [],
   384                    Variables: {
   385                      Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
   386                    },
   387                  },
   388                  {
   389                    Name: 'pablo',
   390                    Capabilities: [],
   391                    Variables: {
   392                      Paths: [{ Capabilities: ['destroy'], PathSpec: 'foo/bar' }],
   393                    },
   394                  },
   395                ],
   396              },
   397            },
   398          ],
   399        });
   400  
   401        this.owner.register('service:token', mockToken);
   402        this.ability.path = 'foo/bar';
   403        this.ability.namespace = 'pablo';
   404  
   405        assert.ok(this.ability.canDestroy);
   406      });
   407    });
   408  
   409    module('#read', function () {
   410      test('it does not permit reading variables by default', function (assert) {
   411        const mockToken = Service.extend({
   412          aclEnabled: true,
   413        });
   414  
   415        this.owner.register('service:token', mockToken);
   416  
   417        assert.notOk(this.ability.canRead);
   418      });
   419  
   420      test('it permits reading variables when token type is management', function (assert) {
   421        const mockToken = Service.extend({
   422          aclEnabled: true,
   423          selfToken: { type: 'management' },
   424        });
   425  
   426        this.owner.register('service:token', mockToken);
   427  
   428        assert.ok(this.ability.canRead);
   429      });
   430  
   431      test('it permits reading variables when acl is disabled', function (assert) {
   432        const mockToken = Service.extend({
   433          aclEnabled: false,
   434          selfToken: { type: 'client' },
   435        });
   436  
   437        this.owner.register('service:token', mockToken);
   438  
   439        assert.ok(this.ability.canRead);
   440      });
   441  
   442      test('it permits reading variables when token has Variables with read capabilities in its rules', function (assert) {
   443        const mockToken = Service.extend({
   444          aclEnabled: true,
   445          selfToken: { type: 'client' },
   446          selfTokenPolicies: [
   447            {
   448              rulesJSON: {
   449                Namespaces: [
   450                  {
   451                    Name: 'default',
   452                    Capabilities: [],
   453                    Variables: {
   454                      Paths: [{ Capabilities: ['read'], PathSpec: '*' }],
   455                    },
   456                  },
   457                ],
   458              },
   459            },
   460          ],
   461        });
   462  
   463        this.owner.register('service:token', mockToken);
   464  
   465        assert.ok(this.ability.canRead);
   466      });
   467  
   468      test('it handles namespace matching', function (assert) {
   469        const mockToken = Service.extend({
   470          aclEnabled: true,
   471          selfToken: { type: 'client' },
   472          selfTokenPolicies: [
   473            {
   474              rulesJSON: {
   475                Namespaces: [
   476                  {
   477                    Name: 'default',
   478                    Capabilities: [],
   479                    Variables: {
   480                      Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
   481                    },
   482                  },
   483                  {
   484                    Name: 'pablo',
   485                    Capabilities: [],
   486                    Variables: {
   487                      Paths: [{ Capabilities: ['read'], PathSpec: 'foo/bar' }],
   488                    },
   489                  },
   490                ],
   491              },
   492            },
   493          ],
   494        });
   495  
   496        this.owner.register('service:token', mockToken);
   497        this.ability.path = 'foo/bar';
   498        this.ability.namespace = 'pablo';
   499  
   500        assert.ok(this.ability.canRead);
   501      });
   502    });
   503  
   504    module('#_nearestMatchingPath', function () {
   505      test('returns capabilities for an exact path match', function (assert) {
   506        const mockToken = Service.extend({
   507          aclEnabled: true,
   508          selfToken: { type: 'client' },
   509          selfTokenPolicies: [
   510            {
   511              rulesJSON: {
   512                Namespaces: [
   513                  {
   514                    Name: 'default',
   515                    Capabilities: [],
   516                    Variables: {
   517                      Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
   518                    },
   519                  },
   520                ],
   521              },
   522            },
   523          ],
   524        });
   525  
   526        this.owner.register('service:token', mockToken);
   527        const path = 'foo';
   528  
   529        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   530  
   531        assert.equal(
   532          nearestMatchingPath,
   533          'foo',
   534          'It should return the exact path match.'
   535        );
   536      });
   537  
   538      test('returns capabilities for the nearest fuzzy match if no exact match', function (assert) {
   539        const mockToken = Service.extend({
   540          aclEnabled: true,
   541          selfToken: { type: 'client' },
   542          selfTokenPolicies: [
   543            {
   544              rulesJSON: {
   545                Namespaces: [
   546                  {
   547                    Name: 'default',
   548                    Capabilities: [],
   549                    Variables: {
   550                      Paths: [
   551                        { Capabilities: ['write'], PathSpec: 'foo/*' },
   552                        { Capabilities: ['write'], PathSpec: 'foo/bar/*' },
   553                      ],
   554                    },
   555                  },
   556                ],
   557              },
   558            },
   559          ],
   560        });
   561  
   562        this.owner.register('service:token', mockToken);
   563        const path = 'foo/bar/baz';
   564  
   565        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   566  
   567        assert.equal(
   568          nearestMatchingPath,
   569          'foo/bar/*',
   570          'It should return the nearest fuzzy matching path.'
   571        );
   572      });
   573  
   574      test('handles wildcard prefix matches', function (assert) {
   575        const mockToken = Service.extend({
   576          aclEnabled: true,
   577          selfToken: { type: 'client' },
   578          selfTokenPolicies: [
   579            {
   580              rulesJSON: {
   581                Namespaces: [
   582                  {
   583                    Name: 'default',
   584                    Capabilities: [],
   585                    Variables: {
   586                      Paths: [{ Capabilities: ['write'], PathSpec: 'foo/*' }],
   587                    },
   588                  },
   589                ],
   590              },
   591            },
   592          ],
   593        });
   594  
   595        this.owner.register('service:token', mockToken);
   596        const path = 'foo/bar/baz';
   597  
   598        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   599  
   600        assert.equal(
   601          nearestMatchingPath,
   602          'foo/*',
   603          'It should handle wildcard glob.'
   604        );
   605      });
   606  
   607      test('handles wildcard suffix matches', function (assert) {
   608        const mockToken = Service.extend({
   609          aclEnabled: true,
   610          selfToken: { type: 'client' },
   611          selfTokenPolicies: [
   612            {
   613              rulesJSON: {
   614                Namespaces: [
   615                  {
   616                    Name: 'default',
   617                    Capabilities: [],
   618                    Variables: {
   619                      Paths: [
   620                        { Capabilities: ['write'], PathSpec: '*/bar' },
   621                        { Capabilities: ['write'], PathSpec: '*/bar/baz' },
   622                      ],
   623                    },
   624                  },
   625                ],
   626              },
   627            },
   628          ],
   629        });
   630  
   631        this.owner.register('service:token', mockToken);
   632        const path = 'foo/bar/baz';
   633  
   634        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   635  
   636        assert.equal(
   637          nearestMatchingPath,
   638          '*/bar/baz',
   639          'It should return the nearest ancestor matching path.'
   640        );
   641      });
   642  
   643      test('prioritizes wildcard suffix matches over wildcard prefix matches', function (assert) {
   644        const mockToken = Service.extend({
   645          aclEnabled: true,
   646          selfToken: { type: 'client' },
   647          selfTokenPolicies: [
   648            {
   649              rulesJSON: {
   650                Namespaces: [
   651                  {
   652                    Name: 'default',
   653                    Capabilities: [],
   654                    Variables: {
   655                      Paths: [
   656                        { Capabilities: ['write'], PathSpec: '*/bar' },
   657                        { Capabilities: ['write'], PathSpec: 'foo/*' },
   658                      ],
   659                    },
   660                  },
   661                ],
   662              },
   663            },
   664          ],
   665        });
   666  
   667        this.owner.register('service:token', mockToken);
   668        const path = 'foo/bar/baz';
   669  
   670        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   671  
   672        assert.equal(
   673          nearestMatchingPath,
   674          'foo/*',
   675          'It should prioritize suffix glob wildcard of prefix glob wildcard.'
   676        );
   677      });
   678  
   679      test('defaults to the glob path if there is no exact match or wildcard matches', function (assert) {
   680        const mockToken = Service.extend({
   681          aclEnabled: true,
   682          selfToken: { type: 'client' },
   683          selfTokenPolicies: [
   684            {
   685              rulesJSON: {
   686                Namespaces: [
   687                  {
   688                    Name: 'default',
   689                    Capabilities: [],
   690                    Variables: {
   691                      'Path "*"': {
   692                        Capabilities: ['write'],
   693                      },
   694                      'Path "foo"': {
   695                        Capabilities: ['write'],
   696                      },
   697                    },
   698                  },
   699                ],
   700              },
   701            },
   702          ],
   703        });
   704  
   705        this.owner.register('service:token', mockToken);
   706        const path = 'foo/bar/baz';
   707  
   708        const nearestMatchingPath = this.ability._nearestMatchingPath(path);
   709  
   710        assert.equal(
   711          nearestMatchingPath,
   712          '*',
   713          'It should default to glob wildcard if no matches.'
   714        );
   715      });
   716    });
   717  
   718    module('#_doesMatchPattern', function () {
   719      const edgeCaseTest = 'this is a ϗѾ test';
   720  
   721      module('base cases', function () {
   722        test('it handles an empty pattern', function (assert) {
   723          // arrange
   724          const pattern = '';
   725          const emptyPath = '';
   726          const nonEmptyPath = 'a';
   727  
   728          // act
   729          const matchingResult = this.ability._doesMatchPattern(
   730            pattern,
   731            emptyPath
   732          );
   733          const nonMatchingResult = this.ability._doesMatchPattern(
   734            pattern,
   735            nonEmptyPath
   736          );
   737  
   738          // assert
   739          assert.ok(matchingResult, 'Empty pattern should match empty path');
   740          assert.notOk(
   741            nonMatchingResult,
   742            'Empty pattern should not match non-empty path'
   743          );
   744        });
   745  
   746        test('it handles an empty path', function (assert) {
   747          // arrange
   748          const emptyPath = '';
   749          const emptyPattern = '';
   750          const nonEmptyPattern = 'a';
   751  
   752          // act
   753          const matchingResult = this.ability._doesMatchPattern(
   754            emptyPattern,
   755            emptyPath
   756          );
   757          const nonMatchingResult = this.ability._doesMatchPattern(
   758            nonEmptyPattern,
   759            emptyPath
   760          );
   761  
   762          // assert
   763          assert.ok(matchingResult, 'Empty path should match empty pattern');
   764          assert.notOk(
   765            nonMatchingResult,
   766            'Empty path should not match non-empty pattern'
   767          );
   768        });
   769  
   770        test('it handles a pattern without a glob', function (assert) {
   771          // arrange
   772          const path = '/foo';
   773          const matchingPattern = '/foo';
   774          const nonMatchingPattern = '/bar';
   775  
   776          // act
   777          const matchingResult = this.ability._doesMatchPattern(
   778            matchingPattern,
   779            path
   780          );
   781          const nonMatchingResult = this.ability._doesMatchPattern(
   782            nonMatchingPattern,
   783            path
   784          );
   785  
   786          // assert
   787          assert.ok(matchingResult, 'Matches path correctly.');
   788          assert.notOk(nonMatchingResult, 'Does not match non-matching path.');
   789        });
   790  
   791        test('it handles a pattern that is a lone glob', function (assert) {
   792          // arrange
   793          const path = '/foo';
   794          const glob = '*';
   795  
   796          // act
   797          const matchingResult = this.ability._doesMatchPattern(glob, path);
   798  
   799          // assert
   800          assert.ok(matchingResult, 'Matches glob.');
   801        });
   802  
   803        test('it matches on leading glob', function (assert) {
   804          // arrange
   805          const pattern = '*bar';
   806          const matchingPath = 'footbar';
   807          const nonMatchingPath = 'rockthecasba';
   808  
   809          // act
   810          const matchingResult = this.ability._doesMatchPattern(
   811            pattern,
   812            matchingPath
   813          );
   814          const nonMatchingResult = this.ability._doesMatchPattern(
   815            pattern,
   816            nonMatchingPath
   817          );
   818  
   819          // assert
   820          assert.ok(
   821            matchingResult,
   822            'Correctly matches when leading glob and matching path.'
   823          );
   824          assert.notOk(
   825            nonMatchingResult,
   826            'Does not match when leading glob and non-matching path.'
   827          );
   828        });
   829  
   830        test('it matches on trailing glob', function (assert) {
   831          // arrange
   832          const pattern = 'foo*';
   833          const matchingPath = 'footbar';
   834          const nonMatchingPath = 'bar';
   835  
   836          // act
   837          const matchingResult = this.ability._doesMatchPattern(
   838            pattern,
   839            matchingPath
   840          );
   841          const nonMatchingResult = this.ability._doesMatchPattern(
   842            pattern,
   843            nonMatchingPath
   844          );
   845  
   846          // assert
   847          assert.ok(matchingResult, 'Correctly matches on trailing glob.');
   848          assert.notOk(
   849            nonMatchingResult,
   850            'Does not match on trailing glob if pattern does not match.'
   851          );
   852        });
   853  
   854        test('it matches when glob is in middle', function (assert) {
   855          // arrange
   856          const pattern = 'foo*bar';
   857          const matchingPath = 'footbar';
   858          const nonMatchingPath = 'footba';
   859  
   860          // act
   861          const matchingResult = this.ability._doesMatchPattern(
   862            pattern,
   863            matchingPath
   864          );
   865          const nonMatchingResult = this.ability._doesMatchPattern(
   866            pattern,
   867            nonMatchingPath
   868          );
   869  
   870          // assert
   871          assert.ok(
   872            matchingResult,
   873            'Correctly matches on glob in middle of path.'
   874          );
   875          assert.notOk(
   876            nonMatchingResult,
   877            'Does not match on glob in middle of path if not full pattern match.'
   878          );
   879        });
   880      });
   881  
   882      module('matching edge cases', function () {
   883        test('it matches when string is between globs', function (assert) {
   884          // arrange
   885          const pattern = '*is *';
   886  
   887          // act
   888          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   889  
   890          // assert
   891          assert.ok(result);
   892        });
   893  
   894        test('it handles many non-consective globs', function (assert) {
   895          // arrange
   896          const pattern = '*is*a*';
   897  
   898          // act
   899          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   900  
   901          // assert
   902          assert.ok(result);
   903        });
   904  
   905        test('it handles double globs', function (assert) {
   906          // arrange
   907          const pattern = '**test**';
   908  
   909          // act
   910          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   911  
   912          // assert
   913          assert.ok(result);
   914        });
   915  
   916        test('it handles many consecutive globs', function (assert) {
   917          // arrange
   918          const pattern = '**is**a***test*';
   919  
   920          // act
   921          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   922  
   923          // assert
   924          assert.ok(result);
   925        });
   926  
   927        test('it handles white space between globs', function (assert) {
   928          // arrange
   929          const pattern = '* *';
   930  
   931          // act
   932          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   933  
   934          // assert
   935          assert.ok(result);
   936        });
   937  
   938        test('it handles a pattern of only globs', function (assert) {
   939          // arrange
   940          const pattern = '**********';
   941  
   942          // act
   943          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   944  
   945          // assert
   946          assert.ok(result);
   947        });
   948  
   949        test('it handles unicode characters', function (assert) {
   950          // arrange
   951          const pattern = `*Ѿ*`;
   952  
   953          // act
   954          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   955  
   956          // assert
   957          assert.ok(result);
   958        });
   959  
   960        test('it handles mixed ASCII codes', function (assert) {
   961          // arrange
   962          const pattern = `*is a ϗѾ *`;
   963  
   964          // act
   965          const result = this.ability._doesMatchPattern(pattern, edgeCaseTest);
   966  
   967          // assert
   968          assert.ok(result);
   969        });
   970      });
   971  
   972      module('non-matching edge cases', function () {
   973        const failingCases = [
   974          {
   975            case: 'test*',
   976            message: 'Implicit substring match',
   977          },
   978          {
   979            case: '*is',
   980            message: 'Parial match',
   981          },
   982          {
   983            case: '*no*',
   984            message: 'Globs without match between them',
   985          },
   986          {
   987            case: ' ',
   988            message: 'Plain white space',
   989          },
   990          {
   991            case: '* ',
   992            message: 'Trailing white space',
   993          },
   994          {
   995            case: ' *',
   996            message: 'Leading white space',
   997          },
   998          {
   999            case: '*ʤ*',
  1000            message: 'Non-matching unicode',
  1001          },
  1002          {
  1003            case: 'this*this is a test',
  1004            message: 'Repeated prefix',
  1005          },
  1006        ];
  1007  
  1008        failingCases.forEach(({ case: failingPattern, message }) => {
  1009          test('should fail the specified cases', function (assert) {
  1010            const result = this.ability._doesMatchPattern(
  1011              failingPattern,
  1012              edgeCaseTest
  1013            );
  1014            assert.notOk(result, `${message} should not match.`);
  1015          });
  1016        });
  1017      });
  1018    });
  1019  
  1020    module('#_computeLengthDiff', function () {
  1021      test('should return the difference in length between a path and a pattern', function (assert) {
  1022        // arrange
  1023        const path = 'foo';
  1024        const pattern = 'bar';
  1025  
  1026        // act
  1027        const result = this.ability._computeLengthDiff(pattern, path);
  1028  
  1029        // assert
  1030        assert.equal(
  1031          result,
  1032          0,
  1033          'it returns the difference in length between path and pattern'
  1034        );
  1035      });
  1036  
  1037      test('should factor the number of globs into consideration', function (assert) {
  1038        // arrange
  1039        const pattern = 'foo*';
  1040        const path = 'bark';
  1041  
  1042        // act
  1043        const result = this.ability._computeLengthDiff(pattern, path);
  1044  
  1045        // assert
  1046        assert.equal(
  1047          result,
  1048          1,
  1049          'it adds the number of globs in the pattern to the difference'
  1050        );
  1051      });
  1052    });
  1053  
  1054    module('#_smallestDifference', function () {
  1055      test('returns the smallest difference in the list', function (assert) {
  1056        // arrange
  1057        const path = 'foo/bar';
  1058        const matchingPath = 'foo/*';
  1059        const matches = ['*/baz', '*', matchingPath];
  1060  
  1061        // act
  1062        const result = this.ability._smallestDifference(matches, path);
  1063  
  1064        // assert
  1065        assert.equal(
  1066          result,
  1067          matchingPath,
  1068          'It should return the smallest difference path.'
  1069        );
  1070      });
  1071    });
  1072  
  1073    module('#allPaths', function () {
  1074      test('it filters by namespace and shows all matching paths on the namespace', function (assert) {
  1075        const mockToken = Service.extend({
  1076          aclEnabled: true,
  1077          selfToken: { type: 'client' },
  1078          selfTokenPolicies: [
  1079            {
  1080              rulesJSON: {
  1081                Namespaces: [
  1082                  {
  1083                    Name: 'default',
  1084                    Capabilities: [],
  1085                    Variables: {
  1086                      Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
  1087                    },
  1088                  },
  1089                  {
  1090                    Name: 'bar',
  1091                    Capabilities: [],
  1092                    Variables: {
  1093                      Paths: [
  1094                        { Capabilities: ['read', 'write'], PathSpec: 'foo' },
  1095                      ],
  1096                    },
  1097                  },
  1098                ],
  1099              },
  1100            },
  1101          ],
  1102        });
  1103  
  1104        this.owner.register('service:token', mockToken);
  1105        this.ability.namespace = 'bar';
  1106  
  1107        const allPaths = this.ability.allPaths;
  1108  
  1109        assert.deepEqual(
  1110          allPaths,
  1111          [
  1112            {
  1113              capabilities: ['read', 'write'],
  1114              name: 'foo',
  1115            },
  1116          ],
  1117          'It should return the exact path match.'
  1118        );
  1119      });
  1120  
  1121      test('it matches on default if no namespace is selected', function (assert) {
  1122        const mockToken = Service.extend({
  1123          aclEnabled: true,
  1124          selfToken: { type: 'client' },
  1125          selfTokenPolicies: [
  1126            {
  1127              rulesJSON: {
  1128                Namespaces: [
  1129                  {
  1130                    Name: 'default',
  1131                    Capabilities: [],
  1132                    Variables: {
  1133                      Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
  1134                    },
  1135                  },
  1136                  {
  1137                    Name: 'bar',
  1138                    Capabilities: [],
  1139                    Variables: {
  1140                      Paths: [
  1141                        { Capabilities: ['read', 'write'], PathSpec: 'foo' },
  1142                      ],
  1143                    },
  1144                  },
  1145                ],
  1146              },
  1147            },
  1148          ],
  1149        });
  1150  
  1151        this.owner.register('service:token', mockToken);
  1152        this.ability.namespace = undefined;
  1153  
  1154        const allPaths = this.ability.allPaths;
  1155  
  1156        assert.deepEqual(
  1157          allPaths,
  1158          [
  1159            {
  1160              capabilities: ['write'],
  1161              name: 'foo',
  1162            },
  1163          ],
  1164          'It should return the exact path match.'
  1165        );
  1166      });
  1167  
  1168      test('it handles globs in namespaces', function (assert) {
  1169        const mockToken = Service.extend({
  1170          aclEnabled: true,
  1171          selfToken: { type: 'client' },
  1172          selfTokenPolicies: [
  1173            {
  1174              rulesJSON: {
  1175                Namespaces: [
  1176                  {
  1177                    Name: '*',
  1178                    Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
  1179                    Variables: {
  1180                      Paths: [
  1181                        {
  1182                          Capabilities: ['list'],
  1183                          PathSpec: '*',
  1184                        },
  1185                      ],
  1186                    },
  1187                  },
  1188                  {
  1189                    Name: 'namespace-1',
  1190                    Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
  1191                    Variables: {
  1192                      Paths: [
  1193                        {
  1194                          Capabilities: ['list', 'read', 'destroy', 'create'],
  1195                          PathSpec: '*',
  1196                        },
  1197                      ],
  1198                    },
  1199                  },
  1200                  {
  1201                    Name: 'namespace-2',
  1202                    Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
  1203                    Variables: {
  1204                      Paths: [
  1205                        {
  1206                          Capabilities: ['list', 'read', 'destroy', 'create'],
  1207                          PathSpec: 'blue/*',
  1208                        },
  1209                        {
  1210                          Capabilities: ['list', 'read', 'create'],
  1211                          PathSpec: 'nomad/jobs/*',
  1212                        },
  1213                      ],
  1214                    },
  1215                  },
  1216                ],
  1217              },
  1218            },
  1219          ],
  1220        });
  1221  
  1222        this.owner.register('service:token', mockToken);
  1223        this.ability.namespace = 'pablo';
  1224  
  1225        const allPaths = this.ability.allPaths;
  1226  
  1227        assert.deepEqual(
  1228          allPaths,
  1229          [
  1230            {
  1231              capabilities: ['list'],
  1232              name: '*',
  1233            },
  1234          ],
  1235          'It should return the glob matching namespace match.'
  1236        );
  1237      });
  1238    });
  1239  });