github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/ui/tests/unit/adapters/job-test.js (about)

     1  import { run } from '@ember/runloop';
     2  import { assign } from '@ember/polyfills';
     3  import { settled } from '@ember/test-helpers';
     4  import { setupTest } from 'ember-qunit';
     5  import { module, test } from 'qunit';
     6  import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
     7  import XHRToken from 'nomad-ui/utils/classes/xhr-token';
     8  
     9  module('Unit | Adapter | Job', function(hooks) {
    10    setupTest(hooks);
    11  
    12    hooks.beforeEach(async function() {
    13      this.store = this.owner.lookup('service:store');
    14      this.subject = () => this.store.adapterFor('job');
    15  
    16      window.sessionStorage.clear();
    17      window.localStorage.clear();
    18  
    19      this.server = startMirage();
    20  
    21      this.initializeUI = async () => {
    22        this.server.create('namespace');
    23        this.server.create('namespace', { id: 'some-namespace' });
    24        this.server.create('node');
    25        this.server.create('job', { id: 'job-1', namespaceId: 'default' });
    26        this.server.create('job', { id: 'job-2', namespaceId: 'some-namespace' });
    27  
    28        this.server.create('region', { id: 'region-1' });
    29        this.server.create('region', { id: 'region-2' });
    30  
    31        this.system = this.owner.lookup('service:system');
    32  
    33        // Namespace, default region, and all regions are requests that all
    34        // job requests depend on. Fetching them ahead of time means testing
    35        // job adapter behavior in isolation.
    36        await this.system.get('namespaces');
    37        this.system.get('shouldIncludeRegion');
    38        await this.system.get('defaultRegion');
    39  
    40        // Reset the handledRequests array to avoid accounting for this
    41        // namespaces request everywhere.
    42        this.server.pretender.handledRequests.length = 0;
    43      };
    44    });
    45  
    46    hooks.afterEach(function() {
    47      this.server.shutdown();
    48    });
    49  
    50    test('The job endpoint is the only required endpoint for fetching a job', async function(assert) {
    51      await this.initializeUI();
    52  
    53      const { pretender } = this.server;
    54      const jobName = 'job-1';
    55      const jobNamespace = 'default';
    56      const jobId = JSON.stringify([jobName, jobNamespace]);
    57  
    58      this.subject().findRecord(null, { modelName: 'job' }, jobId);
    59  
    60      assert.deepEqual(
    61        pretender.handledRequests.mapBy('url'),
    62        [`/v1/job/${jobName}`],
    63        'The only request made is /job/:id'
    64      );
    65    });
    66  
    67    test('When a namespace is set in localStorage but a job in the default namespace is requested, the namespace query param is not present', async function(assert) {
    68      window.localStorage.nomadActiveNamespace = 'some-namespace';
    69  
    70      await this.initializeUI();
    71  
    72      const { pretender } = this.server;
    73      const jobName = 'job-1';
    74      const jobNamespace = 'default';
    75      const jobId = JSON.stringify([jobName, jobNamespace]);
    76  
    77      this.subject().findRecord(null, { modelName: 'job' }, jobId);
    78  
    79      assert.deepEqual(
    80        pretender.handledRequests.mapBy('url'),
    81        [`/v1/job/${jobName}`],
    82        'The only request made is /job/:id with no namespace query param'
    83      );
    84    });
    85  
    86    test('When a namespace is in localStorage and the requested job is in the default namespace, the namespace query param is left out', async function(assert) {
    87      window.localStorage.nomadActiveNamespace = 'red-herring';
    88  
    89      await this.initializeUI();
    90  
    91      const { pretender } = this.server;
    92      const jobName = 'job-1';
    93      const jobNamespace = 'default';
    94      const jobId = JSON.stringify([jobName, jobNamespace]);
    95  
    96      this.subject().findRecord(null, { modelName: 'job' }, jobId);
    97  
    98      assert.deepEqual(
    99        pretender.handledRequests.mapBy('url'),
   100        [`/v1/job/${jobName}`],
   101        'The request made is /job/:id with no namespace query param'
   102      );
   103    });
   104  
   105    test('When the job has a namespace other than default, it is in the URL', async function(assert) {
   106      await this.initializeUI();
   107  
   108      const { pretender } = this.server;
   109      const jobName = 'job-2';
   110      const jobNamespace = 'some-namespace';
   111      const jobId = JSON.stringify([jobName, jobNamespace]);
   112  
   113      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   114  
   115      assert.deepEqual(
   116        pretender.handledRequests.mapBy('url'),
   117        [`/v1/job/${jobName}?namespace=${jobNamespace}`],
   118        'The only request made is /job/:id?namespace=:namespace'
   119      );
   120    });
   121  
   122    test('When there is no token set in the token service, no x-nomad-token header is set', async function(assert) {
   123      await this.initializeUI();
   124  
   125      const { pretender } = this.server;
   126      const jobId = JSON.stringify(['job-1', 'default']);
   127  
   128      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   129  
   130      assert.notOk(
   131        pretender.handledRequests.mapBy('requestHeaders').some(headers => headers['X-Nomad-Token']),
   132        'No token header present on either job request'
   133      );
   134    });
   135  
   136    test('When a token is set in the token service, then x-nomad-token header is set', async function(assert) {
   137      await this.initializeUI();
   138  
   139      const { pretender } = this.server;
   140      const jobId = JSON.stringify(['job-1', 'default']);
   141      const secret = 'here is the secret';
   142  
   143      this.subject().set('token.secret', secret);
   144      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   145  
   146      assert.ok(
   147        pretender.handledRequests
   148          .mapBy('requestHeaders')
   149          .every(headers => headers['X-Nomad-Token'] === secret),
   150        'The token header is present on both job requests'
   151      );
   152    });
   153  
   154    test('findAll can be watched', async function(assert) {
   155      await this.initializeUI();
   156  
   157      const { pretender } = this.server;
   158  
   159      const request = () =>
   160        this.subject().findAll(null, { modelName: 'job' }, null, {
   161          reload: true,
   162          adapterOptions: { watch: true },
   163        });
   164  
   165      request();
   166      assert.equal(
   167        pretender.handledRequests[0].url,
   168        '/v1/jobs?index=1',
   169        'Second request is a blocking request for jobs'
   170      );
   171  
   172      await settled();
   173      request();
   174      assert.equal(
   175        pretender.handledRequests[1].url,
   176        '/v1/jobs?index=2',
   177        'Third request is a blocking request with an incremented index param'
   178      );
   179  
   180      await settled();
   181    });
   182  
   183    test('findRecord can be watched', async function(assert) {
   184      await this.initializeUI();
   185  
   186      const jobId = JSON.stringify(['job-1', 'default']);
   187      const { pretender } = this.server;
   188  
   189      const request = () =>
   190        this.subject().findRecord(null, { modelName: 'job' }, jobId, {
   191          reload: true,
   192          adapterOptions: { watch: true },
   193        });
   194  
   195      request();
   196      assert.equal(
   197        pretender.handledRequests[0].url,
   198        '/v1/job/job-1?index=1',
   199        'Second request is a blocking request for job-1'
   200      );
   201  
   202      await settled();
   203      request();
   204      assert.equal(
   205        pretender.handledRequests[1].url,
   206        '/v1/job/job-1?index=2',
   207        'Third request is a blocking request with an incremented index param'
   208      );
   209  
   210      await settled();
   211    });
   212  
   213    test('relationships can be reloaded', async function(assert) {
   214      await this.initializeUI();
   215  
   216      const { pretender } = this.server;
   217      const plainId = 'job-1';
   218      const mockModel = makeMockModel(plainId);
   219  
   220      this.subject().reloadRelationship(mockModel, 'summary');
   221      await settled();
   222      assert.equal(
   223        pretender.handledRequests[0].url,
   224        `/v1/job/${plainId}/summary`,
   225        'Relationship was reloaded'
   226      );
   227    });
   228  
   229    test('relationship reloads can be watched', async function(assert) {
   230      await this.initializeUI();
   231  
   232      const { pretender } = this.server;
   233      const plainId = 'job-1';
   234      const mockModel = makeMockModel(plainId);
   235  
   236      this.subject().reloadRelationship(mockModel, 'summary', { watch: true });
   237      assert.equal(
   238        pretender.handledRequests[0].url,
   239        '/v1/job/job-1/summary?index=1',
   240        'First request is a blocking request for job-1 summary relationship'
   241      );
   242  
   243      await settled();
   244      this.subject().reloadRelationship(mockModel, 'summary', { watch: true });
   245      assert.equal(
   246        pretender.handledRequests[1].url,
   247        '/v1/job/job-1/summary?index=2',
   248        'Second request is a blocking request with an incremented index param'
   249      );
   250    });
   251  
   252    test('findAll can be canceled', async function(assert) {
   253      await this.initializeUI();
   254  
   255      const { pretender } = this.server;
   256      const token = new XHRToken();
   257  
   258      pretender.get('/v1/jobs', () => [200, {}, '[]'], true);
   259  
   260      this.subject()
   261        .findAll(null, { modelName: 'job' }, null, {
   262          reload: true,
   263          adapterOptions: { watch: true, abortToken: token },
   264        })
   265        .catch(() => {});
   266  
   267      const { request: xhr } = pretender.requestReferences[0];
   268      assert.equal(xhr.status, 0, 'Request is still pending');
   269  
   270      // Schedule the cancelation before waiting
   271      run.next(() => {
   272        token.abort();
   273      });
   274  
   275      await settled();
   276      assert.ok(xhr.aborted, 'Request was aborted');
   277    });
   278  
   279    test('findRecord can be canceled', async function(assert) {
   280      await this.initializeUI();
   281  
   282      const { pretender } = this.server;
   283      const jobId = JSON.stringify(['job-1', 'default']);
   284      const token = new XHRToken();
   285  
   286      pretender.get('/v1/job/:id', () => [200, {}, '{}'], true);
   287  
   288      this.subject().findRecord(null, { modelName: 'job' }, jobId, {
   289        reload: true,
   290        adapterOptions: { watch: true, abortToken: token },
   291      });
   292  
   293      const { request: xhr } = pretender.requestReferences[0];
   294      assert.equal(xhr.status, 0, 'Request is still pending');
   295  
   296      // Schedule the cancelation before waiting
   297      run.next(() => {
   298        token.abort();
   299      });
   300  
   301      await settled();
   302      assert.ok(xhr.aborted, 'Request was aborted');
   303    });
   304  
   305    test('relationship reloads can be canceled', async function(assert) {
   306      await this.initializeUI();
   307  
   308      const { pretender } = this.server;
   309      const plainId = 'job-1';
   310      const token = new XHRToken();
   311      const mockModel = makeMockModel(plainId);
   312      pretender.get('/v1/job/:id/summary', () => [200, {}, '{}'], true);
   313  
   314      this.subject().reloadRelationship(mockModel, 'summary', { watch: true, abortToken: token });
   315  
   316      const { request: xhr } = pretender.requestReferences[0];
   317      assert.equal(xhr.status, 0, 'Request is still pending');
   318  
   319      // Schedule the cancelation before waiting
   320      run.next(() => {
   321        token.abort();
   322      });
   323  
   324      await settled();
   325      assert.ok(xhr.aborted, 'Request was aborted');
   326    });
   327  
   328    test('requests can be canceled even if multiple requests for the same URL were made', async function(assert) {
   329      await this.initializeUI();
   330  
   331      const { pretender } = this.server;
   332      const jobId = JSON.stringify(['job-1', 'default']);
   333      const token1 = new XHRToken();
   334      const token2 = new XHRToken();
   335  
   336      pretender.get('/v1/job/:id', () => [200, {}, '{}'], true);
   337  
   338      this.subject().findRecord(null, { modelName: 'job' }, jobId, {
   339        reload: true,
   340        adapterOptions: { watch: true, abortToken: token1 },
   341      });
   342  
   343      this.subject().findRecord(null, { modelName: 'job' }, jobId, {
   344        reload: true,
   345        adapterOptions: { watch: true, abortToken: token2 },
   346      });
   347  
   348      const { request: xhr } = pretender.requestReferences[0];
   349      const { request: xhr2 } = pretender.requestReferences[1];
   350      assert.equal(xhr.status, 0, 'Request is still pending');
   351      assert.equal(pretender.requestReferences.length, 2, 'Two findRecord requests were made');
   352      assert.equal(
   353        pretender.requestReferences.mapBy('url').uniq().length,
   354        1,
   355        'The two requests have the same URL'
   356      );
   357  
   358      // Schedule the cancelation and resolution before waiting
   359      run.next(() => {
   360        token1.abort();
   361        pretender.resolve(xhr2);
   362      });
   363  
   364      await settled();
   365      assert.ok(xhr.aborted, 'Request one was aborted');
   366      assert.notOk(xhr2.aborted, 'Request two was not aborted');
   367    });
   368  
   369    test('when there is no region set, requests are made without the region query param', async function(assert) {
   370      await this.initializeUI();
   371  
   372      const { pretender } = this.server;
   373      const jobName = 'job-1';
   374      const jobNamespace = 'default';
   375      const jobId = JSON.stringify([jobName, jobNamespace]);
   376  
   377      await settled();
   378      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   379      this.subject().findAll(null, { modelName: 'job' }, null);
   380  
   381      assert.deepEqual(
   382        pretender.handledRequests.mapBy('url'),
   383        [`/v1/job/${jobName}`, '/v1/jobs'],
   384        'No requests include the region query param'
   385      );
   386    });
   387  
   388    test('when there is a region set, requests are made with the region query param', async function(assert) {
   389      const region = 'region-2';
   390      window.localStorage.nomadActiveRegion = region;
   391  
   392      await this.initializeUI();
   393  
   394      const { pretender } = this.server;
   395      const jobName = 'job-1';
   396      const jobNamespace = 'default';
   397      const jobId = JSON.stringify([jobName, jobNamespace]);
   398  
   399      await settled();
   400      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   401      this.subject().findAll(null, { modelName: 'job' }, null);
   402  
   403      assert.deepEqual(
   404        pretender.handledRequests.mapBy('url'),
   405        [`/v1/job/${jobName}?region=${region}`, `/v1/jobs?region=${region}`],
   406        'Requests include the region query param'
   407      );
   408    });
   409  
   410    test('when the region is set to the default region, requests are made without the region query param', async function(assert) {
   411      window.localStorage.nomadActiveRegion = 'region-1';
   412  
   413      await this.initializeUI();
   414  
   415      const { pretender } = this.server;
   416      const jobName = 'job-1';
   417      const jobNamespace = 'default';
   418      const jobId = JSON.stringify([jobName, jobNamespace]);
   419  
   420      await settled();
   421      this.subject().findRecord(null, { modelName: 'job' }, jobId);
   422      this.subject().findAll(null, { modelName: 'job' }, null);
   423  
   424      assert.deepEqual(
   425        pretender.handledRequests.mapBy('url'),
   426        [`/v1/job/${jobName}`, '/v1/jobs'],
   427        'No requests include the region query param'
   428      );
   429    });
   430  });
   431  
   432  function makeMockModel(id, options) {
   433    return assign(
   434      {
   435        relationshipFor(name) {
   436          return {
   437            kind: 'belongsTo',
   438            type: 'job-summary',
   439            key: name,
   440          };
   441        },
   442        belongsTo(name) {
   443          return {
   444            link() {
   445              return `/v1/job/${id}/${name}`;
   446            },
   447          };
   448        },
   449      },
   450      options
   451    );
   452  }