summaryrefslogtreecommitdiffstats
path: root/toolkit/components/normandy/test/unit/utils.js
blob: b1273d1d5ed39db2cab6a1f6c1fbe35a9f586352 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"use strict";
/* eslint-disable no-unused-vars */

// Loaded into the same scope as head_xpc.js
/* import-globals-from head_xpc.js */

const { Preferences } = ChromeUtils.importESModule(
  "resource://gre/modules/Preferences.sys.mjs"
);
const { HttpServer } = ChromeUtils.importESModule(
  "resource://testing-common/httpd.sys.mjs"
);

const { NormandyApi } = ChromeUtils.importESModule(
  "resource://normandy/lib/NormandyApi.sys.mjs"
);
const { NormandyTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/NormandyTestUtils.sys.mjs"
);

const CryptoHash = Components.Constructor(
  "@mozilla.org/security/hash;1",
  "nsICryptoHash",
  "initWithString"
);
const FileInputStream = Components.Constructor(
  "@mozilla.org/network/file-input-stream;1",
  "nsIFileInputStream",
  "init"
);

class MockResponse {
  constructor(content) {
    this.content = content;
  }

  async text() {
    return this.content;
  }

  async json() {
    return JSON.parse(this.content);
  }
}

function withServer(server) {
  return function (testFunction) {
    return NormandyTestUtils.decorate(
      NormandyTestUtils.withMockPreferences(),
      async function inner({ mockPreferences, ...args }) {
        const serverUrl = `http://localhost:${server.identity.primaryPort}`;
        mockPreferences.set("app.normandy.api_url", `${serverUrl}/api/v1`);
        NormandyApi.clearIndexCache();

        try {
          await testFunction({ ...args, serverUrl, mockPreferences, server });
        } finally {
          await new Promise(resolve => server.stop(resolve));
        }
      }
    );
  };
}

function makeScriptServer(scriptPath) {
  const server = new HttpServer();
  server.registerContentType("sjs", "sjs");
  server.registerFile("/", do_get_file(scriptPath));
  server.start(-1);
  return server;
}

function withScriptServer(scriptPath) {
  return withServer(makeScriptServer(scriptPath));
}

function makeMockApiServer(directory) {
  const server = new HttpServer();
  server.registerDirectory("/", directory);

  server.setIndexHandler(async function (request, response) {
    response.processAsync();
    const dir = request.getProperty("directory");
    const index = dir.clone();
    index.append("index.json");

    if (!index.exists()) {
      response.setStatusLine("1.1", 404, "Not Found");
      response.write(`Cannot find path ${index.path}`);
      response.finish();
      return;
    }

    try {
      const contents = await IOUtils.readUTF8(index.path);
      response.write(contents);
    } catch (e) {
      response.setStatusLine("1.1", 500, "Server error");
      response.write(e.toString());
    } finally {
      response.finish();
    }
  });

  server.start(-1);
  return server;
}

function withMockApiServer(apiName = "mock_api") {
  return withServer(makeMockApiServer(do_get_file(apiName)));
}

const CryptoUtils = {
  _getHashStringForCrypto(aCrypto) {
    // return the two-digit hexadecimal code for a byte
    let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);

    // convert the binary hash data to a hex string.
    let binary = aCrypto.finish(false);
    let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
    return hash.join("").toLowerCase();
  },

  /**
   * Get the computed hash for a given file
   * @param {nsIFile} file The file to be hashed
   * @param {string} [algorithm] The hashing algorithm to use
   */
  getFileHash(file, algorithm = "sha256") {
    const crypto = CryptoHash(algorithm);
    const fis = new FileInputStream(file, -1, -1, false);
    crypto.updateFromStream(fis, file.fileSize);
    const hash = this._getHashStringForCrypto(crypto);
    fis.close();
    return hash;
  },
};