search-result-entry.test.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. 'use strict'
  2. const tap = require('tap')
  3. const { operations } = require('@ldapjs/protocol')
  4. const Attribute = require('@ldapjs/attribute')
  5. const { DN } = require('@ldapjs/dn')
  6. const { BerWriter, BerReader } = require('@ldapjs/asn1')
  7. const SearchResultEntry = require('./search-result-entry')
  8. const {
  9. searchResultEntryBytes,
  10. searchResultEntryNoValuesBytes
  11. } = require('./_fixtures/message-byte-arrays')
  12. tap.test('basic', t => {
  13. t.test('constructor no args', async t => {
  14. const res = new SearchResultEntry()
  15. t.strictSame(res.pojo, {
  16. messageId: 1,
  17. protocolOp: operations.LDAP_RES_SEARCH_ENTRY,
  18. type: 'SearchResultEntry',
  19. objectName: '',
  20. attributes: [],
  21. controls: []
  22. })
  23. t.equal(res.type, 'SearchResultEntry')
  24. })
  25. t.test('constructor with args', async t => {
  26. const res = new SearchResultEntry({
  27. objectName: 'dc=example,dc=com',
  28. attributes: [{ type: 'cn', values: ['foo'] }]
  29. })
  30. t.strictSame(res.pojo, {
  31. messageId: 1,
  32. protocolOp: operations.LDAP_RES_SEARCH_ENTRY,
  33. type: 'SearchResultEntry',
  34. objectName: 'dc=example,dc=com',
  35. attributes: [{ type: 'cn', values: ['foo'] }],
  36. controls: []
  37. })
  38. })
  39. t.end()
  40. })
  41. tap.test('.attributes', t => {
  42. t.test('sets/gets', async t => {
  43. const res = new SearchResultEntry()
  44. t.strictSame(res.attributes, [])
  45. res.attributes = [new Attribute({ type: 'cn', values: 'foo' })]
  46. t.strictSame(res.attributes, [new Attribute({ type: 'cn', values: 'foo' })])
  47. })
  48. t.test('rejects non-array', async t => {
  49. const res = new SearchResultEntry()
  50. t.throws(
  51. () => {
  52. res.attributes = { type: 'cn', values: ['foo'] }
  53. },
  54. 'attrs must be an array'
  55. )
  56. })
  57. t.test('rejects non-attribute objects', async t => {
  58. const res = new SearchResultEntry()
  59. t.throws(
  60. () => {
  61. res.attributes = [{ foo: 'bar' }]
  62. },
  63. 'attr must be an Attribute instance or Attribute-like object'
  64. )
  65. })
  66. t.end()
  67. })
  68. tap.test('.objectName', t => {
  69. t.test('sets/gets', async t => {
  70. const res = new SearchResultEntry()
  71. t.equal(res.objectName.toString(), '')
  72. t.equal(res._dn.toString(), '')
  73. res.objectName = 'cn=foo'
  74. t.equal(res.objectName.toString(), 'cn=foo')
  75. res.objectName = DN.fromString('sn=bar')
  76. t.equal(res.objectName.toString(), 'sn=bar')
  77. })
  78. t.test('throws for invalid value', async t => {
  79. const res = new SearchResultEntry()
  80. t.throws(
  81. () => {
  82. res.objectName = ['invalid input']
  83. },
  84. 'objectName must be a DN string or instance of LdapDn'
  85. )
  86. })
  87. t.end()
  88. })
  89. tap.test('_toBer', t => {
  90. t.test('converts instance to BER', async t => {
  91. let res = new SearchResultEntry({
  92. objectName: 'dc=example,dc=com',
  93. attributes: [
  94. { type: 'objectClass', values: ['top', 'domain'] },
  95. { type: 'dc', values: ['example'] }
  96. ]
  97. })
  98. let writer = new BerWriter()
  99. res._toBer(writer)
  100. t.equal(
  101. Buffer.from(searchResultEntryBytes.slice(5)).compare(writer.buffer),
  102. 0
  103. )
  104. res = new SearchResultEntry({
  105. objectName: 'dc=example,dc=com',
  106. attributes: [
  107. { type: 'objectClass', values: [] },
  108. { type: 'dc', values: [] }
  109. ]
  110. })
  111. writer = new BerWriter()
  112. res._toBer(writer)
  113. t.equal(
  114. Buffer.from(searchResultEntryNoValuesBytes.slice(5)).compare(writer.buffer),
  115. 0
  116. )
  117. })
  118. t.end()
  119. })
  120. tap.test('_pojo', t => {
  121. t.test('returns a pojo representation', async t => {
  122. const req = new SearchResultEntry({
  123. objectName: 'cn=foo',
  124. attributes: [{ type: 'bar', values: ['baz'] }]
  125. })
  126. t.strictSame(req._pojo(), {
  127. objectName: 'cn=foo',
  128. attributes: [{
  129. type: 'bar',
  130. values: ['baz']
  131. }]
  132. })
  133. })
  134. t.end()
  135. })
  136. tap.test('#parseToPojo', t => {
  137. t.test('throws if operation incorrect', async t => {
  138. const reqBuffer = Buffer.from(searchResultEntryBytes)
  139. reqBuffer[5] = 0x61
  140. const reader = new BerReader(reqBuffer)
  141. reader.readSequence()
  142. reader.readInt()
  143. t.throws(
  144. () => SearchResultEntry.parseToPojo(reader),
  145. Error('found wrong protocol operation: 0x61')
  146. )
  147. })
  148. t.test('returns a pojo representation', async t => {
  149. const reqBuffer = Buffer.from(searchResultEntryBytes)
  150. const reader = new BerReader(reqBuffer)
  151. reader.readSequence()
  152. reader.readInt()
  153. const pojo = SearchResultEntry.parseToPojo(reader)
  154. t.equal(pojo.protocolOp, operations.LDAP_RES_SEARCH_ENTRY)
  155. t.equal(pojo.objectName, 'dc=example,dc=com')
  156. t.strictSame(pojo.attributes[0].pojo, {
  157. type: 'objectClass',
  158. values: ['top', 'domain']
  159. })
  160. t.strictSame(pojo.attributes[1].pojo, {
  161. type: 'dc',
  162. values: ['example']
  163. })
  164. })
  165. t.end()
  166. })