github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/ui/tests/integration/allocation-row-test.js (about)

     1  import { module, test } from 'qunit';
     2  import { setupRenderingTest } from 'ember-qunit';
     3  import { render, settled } from '@ember/test-helpers';
     4  import hbs from 'htmlbars-inline-precompile';
     5  import generateResources from '../../mirage/data/generate-resources';
     6  import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
     7  import { find } from 'ember-native-dom-helpers';
     8  import Response from 'ember-cli-mirage/response';
     9  import { initialize as fragmentSerializerInitializer } from 'nomad-ui/initializers/fragment-serializer';
    10  import { Promise, resolve } from 'rsvp';
    11  
    12  module('Integration | Component | allocation row', function(hooks) {
    13    setupRenderingTest(hooks);
    14  
    15    hooks.beforeEach(function() {
    16      fragmentSerializerInitializer(this.owner);
    17      this.store = this.owner.lookup('service:store');
    18      this.server = startMirage();
    19      this.server.create('namespace');
    20      this.server.create('node');
    21      this.server.create('job', { createAllocations: false });
    22    });
    23  
    24    hooks.afterEach(function() {
    25      this.server.shutdown();
    26    });
    27  
    28    test('Allocation row polls for stats, even when it errors or has an invalid response', function(assert) {
    29      const component = this;
    30  
    31      let currentFrame = 0;
    32      let frames = [
    33        JSON.stringify({ ResourceUsage: generateResources() }),
    34        JSON.stringify({ ResourceUsage: generateResources() }),
    35        null,
    36        '<Not>Valid JSON</Not>',
    37        JSON.stringify({ ResourceUsage: generateResources() }),
    38      ];
    39  
    40      this.server.get('/client/allocation/:id/stats', function() {
    41        const response = frames[++currentFrame];
    42  
    43        // Disable polling to stop the EC task in the component
    44        if (currentFrame >= frames.length) {
    45          component.set('enablePolling', false);
    46        }
    47  
    48        if (response) {
    49          return response;
    50        }
    51        return new Response(500, {}, '');
    52      });
    53  
    54      this.server.create('allocation', { clientStatus: 'running' });
    55      this.store.findAll('allocation');
    56  
    57      let allocation;
    58  
    59      return settled()
    60        .then(async () => {
    61          allocation = this.store.peekAll('allocation').get('firstObject');
    62  
    63          this.setProperties({
    64            allocation,
    65            context: 'job',
    66            enablePolling: true,
    67          });
    68  
    69          await render(hbs`
    70            {{allocation-row
    71              allocation=allocation
    72              context=context
    73              enablePolling=enablePolling}}
    74          `);
    75          return settled();
    76        })
    77        .then(() => {
    78          assert.equal(
    79            this.server.pretender.handledRequests.filterBy(
    80              'url',
    81              `/v1/client/allocation/${allocation.get('id')}/stats`
    82            ).length,
    83            frames.length,
    84            'Requests continue to be made after malformed responses and server errors'
    85          );
    86        });
    87    });
    88  
    89    test('Allocation row shows warning when it requires drivers that are unhealthy on the node it is running on', function(assert) {
    90      const node = this.server.schema.nodes.first();
    91      const drivers = node.drivers;
    92      Object.values(drivers).forEach(driver => {
    93        driver.Healthy = false;
    94        driver.Detected = true;
    95      });
    96      node.update({ drivers });
    97  
    98      this.server.create('allocation', { clientStatus: 'running' });
    99      this.store.findAll('job');
   100      this.store.findAll('node');
   101      this.store.findAll('allocation');
   102  
   103      let allocation;
   104  
   105      return settled()
   106        .then(async () => {
   107          allocation = this.store.peekAll('allocation').get('firstObject');
   108  
   109          this.setProperties({
   110            allocation,
   111            context: 'job',
   112          });
   113  
   114          await render(hbs`
   115            {{allocation-row
   116              allocation=allocation
   117              context=context}}
   118          `);
   119          return settled();
   120        })
   121        .then(() => {
   122          assert.ok(find('[data-test-icon="unhealthy-driver"]'), 'Unhealthy driver icon is shown');
   123        });
   124    });
   125  
   126    test('Allocation row shows an icon indicator when it was preempted', async function(assert) {
   127      const allocId = this.server.create('allocation', 'preempted').id;
   128  
   129      const allocation = await this.store.findRecord('allocation', allocId);
   130      await settled();
   131  
   132      this.setProperties({ allocation, context: 'job' });
   133      await render(hbs`
   134        {{allocation-row
   135          allocation=allocation
   136          context=context}}
   137      `);
   138      await settled();
   139  
   140      assert.ok(find('[data-test-icon="preemption"]'), 'Preempted icon is shown');
   141    });
   142  
   143    test('when an allocation is not running, the utilization graphs are omitted', function(assert) {
   144      this.setProperties({
   145        context: 'job',
   146        enablePolling: false,
   147      });
   148  
   149      // All non-running statuses need to be tested
   150      ['pending', 'complete', 'failed', 'lost'].forEach(clientStatus =>
   151        this.server.create('allocation', { clientStatus })
   152      );
   153  
   154      this.store.findAll('allocation');
   155  
   156      return settled().then(() => {
   157        const allocations = this.store.peekAll('allocation');
   158        return waitForEach(
   159          allocations.map(allocation => async () => {
   160            this.set('allocation', allocation);
   161            await render(hbs`
   162                {{allocation-row
   163                  allocation=allocation
   164                  context=context
   165                  enablePolling=enablePolling}}
   166              `);
   167            return settled().then(() => {
   168              const status = allocation.get('clientStatus');
   169              assert.notOk(find('[data-test-cpu] .inline-chart'), `No CPU chart for ${status}`);
   170              assert.notOk(find('[data-test-mem] .inline-chart'), `No Mem chart for ${status}`);
   171            });
   172          })
   173        );
   174      });
   175    });
   176  
   177    // A way to loop over asynchronous code. Can be replaced by async/await in the future.
   178    const waitForEach = fns => {
   179      let i = 0;
   180      let done = () => {};
   181  
   182      // This function is asynchronous and needs to return a promise
   183      const pending = new Promise(resolve => {
   184        done = resolve;
   185      });
   186  
   187      const step = () => {
   188        // The waitForEach promise and this recursive loop are done once
   189        // all functions have been called.
   190        if (i >= fns.length) {
   191          done();
   192          return;
   193        }
   194        // Call the current function
   195        const promise = fns[i]() || resolve(true);
   196        // Increment the function position
   197        i++;
   198        // Wait for async behaviors to settle and repeat
   199        promise.then(() => settled()).then(step);
   200      };
   201  
   202      step();
   203  
   204      return pending;
   205    };
   206  });