609 lines
22 KiB
JavaScript
609 lines
22 KiB
JavaScript
/* istanbul ignore file */
|
||
const expect = require( 'chai' ).expect
|
||
const crs = require( 'crypto-random-string' )
|
||
|
||
const appRoot = require( 'app-root-path' )
|
||
const { beforeEachContext } = require( `${appRoot}/test/hooks` )
|
||
const { createUser } = require( `${appRoot}/modules/core/services/users` )
|
||
const { createStream } = require( `${appRoot}/modules/core/services/streams` )
|
||
const { createCommitByBranchName } = require( `${appRoot}/modules/core/services/commits` )
|
||
|
||
const { createObject } = require( `${appRoot}/modules/core/services/objects` )
|
||
const { createComment, getComments, getComment, viewComment, archiveComment } = require( '../services' )
|
||
|
||
describe( 'Comments @comments', () => {
|
||
let user = {
|
||
name: 'The comment wizard',
|
||
email: 'comment@wizard.ry',
|
||
password: 'i did not like Rivendel wine :('
|
||
}
|
||
|
||
let otherUser = {
|
||
name: 'Fondalf The Brey',
|
||
email: 'totalnotfakegandalf87@mordor.com',
|
||
password: 'what gandalf puts in his pipe stays in his pipe'
|
||
}
|
||
|
||
let stream = {
|
||
name: 'Commented stream',
|
||
description: 'Chit chats over here'
|
||
}
|
||
|
||
let testObject1 = {
|
||
foo: 'bar'
|
||
}
|
||
|
||
let testObject2 = {
|
||
foo: 'barbar',
|
||
baz: 123
|
||
}
|
||
let commitId1, commitId2
|
||
|
||
before( async () => {
|
||
await beforeEachContext()
|
||
|
||
user.id = await createUser( user )
|
||
otherUser.id = await createUser( otherUser )
|
||
|
||
stream.id = await createStream( { ...stream, ownerId: user.id } )
|
||
|
||
testObject1.id = await createObject( stream.id, testObject1 )
|
||
testObject2.id = await createObject( stream.id, testObject2 )
|
||
|
||
commitId1 = await createCommitByBranchName( { streamId: stream.id, branchName: 'main', message: 'first commit', sourceApplication: 'tests', objectId: testObject1.id, authorId: user.id } )
|
||
commitId2 = await createCommitByBranchName( { streamId: stream.id, branchName: 'main', message: 'first commit', sourceApplication: 'tests', objectId: testObject2.id, authorId: user.id } )
|
||
} )
|
||
|
||
it( 'Should not be allowed to comment without specifying at least one target resource', async () => {
|
||
return await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.be.equal( 'Must specify at least one resource as the comment target' ) )
|
||
} )
|
||
it( 'Should not be able to comment resources that do not belong to the input streamId', async () => {
|
||
// need to check streamId - commit link
|
||
// need to check streamId - object link
|
||
// need to check streamId - stream match
|
||
// need to check comment reply recursively? that sounds too much of an effort
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: 'almost the stream.id', resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.be.equal( 'Input streamId doesn\'t match the stream resource.resourceId' ) )
|
||
|
||
//add the checks from above
|
||
expect( 1 ).to.equal( 2 )
|
||
} )
|
||
|
||
it('Should create viewedAt entries for comments', async () => {
|
||
const id = await createComment({
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [{ resourceId: commitId1, resourceType: 'commit' }],
|
||
text: 'https://tenor.com/view/gandalf-smoking-gif-21189890', // possibly NSFW
|
||
data: { someMore: 'https://tenor.com/view/gandalf-old-man-naked-take-robe-off-funny-gif-17224126' } // possibly NSFW
|
||
}
|
||
})
|
||
|
||
// ppl creating comments get to view them too
|
||
const comment = await getComment({id, userId: user.id})
|
||
expect(comment).to.haveOwnProperty('viewedAt')
|
||
|
||
const commentNoUser = await getComment({id})
|
||
expect(commentNoUser).to.not.haveOwnProperty('viewedAt')
|
||
|
||
const commentOtherUser = await getComment({id, userId: otherUser.id})
|
||
expect(commentOtherUser.viewedAt).to.be.null
|
||
|
||
await viewComment({userId: user.id, commentId: id })
|
||
|
||
const viewedCommentOtherUser = await getComment({id, userId: otherUser.id})
|
||
expect(viewedCommentOtherUser).to.haveOwnProperty('viewedAt')
|
||
})
|
||
|
||
it( 'Should not be allowed to comment targeting multiple streams as a resource', async () => {
|
||
return await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.be.equal( 'Commenting on multiple streams is not supported' ) )
|
||
} )
|
||
|
||
it( 'Should not be allowed to comment on non existing resources', async () => {
|
||
const nonExistentResources = [
|
||
{
|
||
streamId: 'this doesnt exist dummy',
|
||
resources: [
|
||
{ resourceId: 'this doesnt exist dummy', resourceType: 'stream' },
|
||
],
|
||
text: null,
|
||
data: null
|
||
},
|
||
{
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: 'this doesnt exist dummy', resourceType: 'commit' },
|
||
],
|
||
text: null,
|
||
data: null
|
||
},
|
||
{
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: 'this doesnt exist dummy', resourceType: 'object' },
|
||
],
|
||
text: null,
|
||
data: null
|
||
},
|
||
{
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: 'this doesnt exist dummy', resourceType: 'comment' },
|
||
],
|
||
text: null,
|
||
data: null
|
||
},
|
||
]
|
||
for ( const input of nonExistentResources ) {
|
||
await createComment( { userId: user.id, input } )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.contain( ': this doesnt exist dummy doesn\'t exist, you cannot comment on it' ) )
|
||
}
|
||
} )
|
||
|
||
it( 'Should not be allowed to comment on an non supported resource type', async () => {
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: 'jubbjabb', resourceType: 'flux capacitor' },
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.equal( 'resource type flux capacitor is not supported as a comment target' ) )
|
||
} )
|
||
|
||
it( 'Should be able to comment on valid resources in any permutation', async () => {
|
||
const resourceCombinations = [
|
||
[
|
||
{ resourceId: stream.id, resourceType: 'stream' }
|
||
],
|
||
[
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' }
|
||
],
|
||
[
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' }
|
||
],
|
||
[
|
||
// object overlay on object
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' },
|
||
{ resourceId: testObject2.id, resourceType: 'object' }
|
||
],
|
||
[
|
||
// an object overlayed on a commit
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: testObject2.id, resourceType: 'object' }
|
||
],
|
||
[
|
||
// an object overlayed on a commit
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' },
|
||
{ resourceId: testObject2.id, resourceType: 'object' }
|
||
],
|
||
[
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: commitId2, resourceType: 'commit' },
|
||
{ resourceId: testObject1.id, resourceType: 'object' }
|
||
]
|
||
]
|
||
|
||
|
||
// yeah i know, Promise.all, but this is easier to debug...
|
||
for ( const resources of resourceCombinations ) {
|
||
const commentId = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources,
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
expect( commentId ).to.exist
|
||
}
|
||
} )
|
||
|
||
it( 'Should not return the same comment multiple times for multi resource comments', async () => {
|
||
const localObjectId = await createObject( stream.id, { testObject: 1 } )
|
||
|
||
const commentCount = 3
|
||
for ( let i = 0; i < commentCount; i++ ) {
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: 'distinct test' + crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
}
|
||
|
||
const comments = await getComments( {
|
||
streamId: stream.id, resources: [
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
]
|
||
} )
|
||
|
||
let ids = comments.items.map( c => c.id)
|
||
let set = new Set( ids )
|
||
expect(set.size).to.equal(ids.length)
|
||
|
||
// Note: since we switched to an "or" clause, this does not apply anymore.
|
||
// expect( comments.items ).to.have.lengthOf( commentCount )
|
||
} )
|
||
|
||
it( 'Should handle cursor and limit for queries', async () => {
|
||
const localObjectId = await createObject( stream.id, { testObject: 'something completely different' } )
|
||
|
||
let createdComments = []
|
||
const commentCount = 10
|
||
for ( let i = 0; i < commentCount; i++ ) {
|
||
createdComments.push( await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} ) )
|
||
await new Promise( resolve => setTimeout( resolve, 50 ) )
|
||
}
|
||
|
||
let comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
limit: 2
|
||
} )
|
||
expect( comments.items ).to.have.lengthOf( 2 )
|
||
expect( createdComments.reverse().slice( 0, 2 ) ).deep.to.equal( comments.items.map( c => c.id ) ) // note: reversing as default order is newest first now
|
||
|
||
const cursor = comments.items[1].createdAt
|
||
comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
limit: 2,
|
||
cursor
|
||
} )
|
||
expect( comments.items ).to.have.lengthOf( 2 )
|
||
expect( createdComments.slice( 2, 4 ) ).deep.to.equal( comments.items.map( c => c.id ) )
|
||
} )
|
||
|
||
it( 'Should properly return replies for a comment', async () => {
|
||
const streamCommentId1 = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
|
||
const commentId1 = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: streamCommentId1, resourceType: 'comment' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
|
||
const commentId2 = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: streamCommentId1, resourceType: 'comment' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
const replies = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: streamCommentId1, resourceType: 'comment' },
|
||
],
|
||
} )
|
||
expect( replies.items ).to.have.lengthOf( 2 )
|
||
expect( replies.items.reverse().map( i => i.id ) ).deep.to.equal( [ commentId1, commentId2 ] )
|
||
} )
|
||
|
||
it( 'Should return all the referenced resources for a comment', async () => {
|
||
const localObjectId = await createObject( stream.id, { anotherTestObject: 1 } )
|
||
const inputResources = [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: commitId1, resourceType: 'commit' },
|
||
{ resourceId: localObjectId, resourceType: 'object' },
|
||
{ resourceId: testObject2.id, resourceType: 'object' }
|
||
]
|
||
const queryResources = [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: localObjectId, resourceType: 'object' },
|
||
]
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: inputResources,
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
|
||
const comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: queryResources
|
||
} )
|
||
// expect( comments.items ).to.have.lengthOf( 1 ) // not applicable anymore, as we're "OR"-ing
|
||
let resIds = comments.items[0].resources.map(r => r.resourceId ).sort()
|
||
inputResources.sort() // order is not ensured
|
||
expect( comments.items[0].resources ).to.have.deep.members( inputResources )
|
||
} )
|
||
|
||
it( 'Should return the same data when querying a single comment vs a list of comments', async () => {
|
||
const localObjectId = await createObject( stream.id, { anotherTestObject: 42 } )
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
{ resourceId: localObjectId, resourceType: 'object' },
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
const comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: localObjectId, resourceType: 'object' },
|
||
]
|
||
} )
|
||
expect( comments.items ).to.have.lengthOf( 1 )
|
||
const [ firstComment ] = comments.items
|
||
const comment = await getComment( { id: firstComment.id } )
|
||
|
||
// the getComments query brings along some extra garbage i'm lazy to clean up
|
||
delete firstComment.total_count
|
||
delete firstComment.resourceType
|
||
delete firstComment.resourceId
|
||
delete firstComment.commentId
|
||
|
||
expect( comment ).deep.to.equal( firstComment )
|
||
} )
|
||
|
||
it( 'Should be able to edit a comment text and its context???' )
|
||
|
||
it( 'Should not be allowed to edit a not existing comment' )
|
||
|
||
it( 'Should be able to archive a comment', async () => {
|
||
const commentId = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' },
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
|
||
let comment = await getComment( {id: commentId} )
|
||
expect( comment.archived ).to.equal( false )
|
||
|
||
await archiveComment( { commentId } )
|
||
|
||
comment = await getComment( { id: commentId } )
|
||
expect( comment.archived ).to.equal( true )
|
||
|
||
await archiveComment( { commentId, archived: false } )
|
||
|
||
comment = await getComment( {id: commentId} )
|
||
expect( comment.archived ).to.equal( false )
|
||
} )
|
||
|
||
it( 'Should not be allowed to archive a not existing comment', async () => {
|
||
archiveComment( { commentId: 'badabumm' } )
|
||
.then( () => { throw new Error( 'This should have been rejected' ) } )
|
||
.catch( error => expect( error.message ).to.be.equal( 'No comment badabumm exists, cannot change its archival status' ) )
|
||
} )
|
||
|
||
it( 'Should not query archived comments unless asked', async () => {
|
||
const localObjectId = await createObject( stream.id, { testObject: crs( { length: 10 } ) } )
|
||
|
||
const commentCount = 15
|
||
for ( let i = 0; i < commentCount; i++ ) {
|
||
await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
text: crs( { length: 10 } ),
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
}
|
||
|
||
const archiveCount = 3
|
||
let comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
limit: archiveCount
|
||
} )
|
||
expect( comments.totalCount ).to.be.equal( commentCount )
|
||
|
||
await Promise.all( comments.items.map( comment => archiveComment( { commentId: comment.id } ) ) )
|
||
|
||
comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
limit: 100
|
||
} )
|
||
expect( comments.totalCount ).to.be.equal( commentCount - archiveCount )
|
||
expect( comments.items.length ).to.be.equal( commentCount - archiveCount )
|
||
|
||
comments = await getComments( {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: localObjectId, resourceType: 'object' }
|
||
],
|
||
limit: 100,
|
||
archived: true
|
||
} )
|
||
expect( comments.totalCount ).to.be.equal( archiveCount )
|
||
expect( comments.items.length ).to.be.equal( archiveCount )
|
||
} )
|
||
it( 'Should publish events to pubsub, test it by registering a subscriber' )
|
||
|
||
it( 'Should be able to write a short novel as comment text', async () => {
|
||
const commentId = await createComment( {
|
||
userId: user.id,
|
||
input: {
|
||
streamId: stream.id,
|
||
resources: [
|
||
{ resourceId: stream.id, resourceType: 'stream' }
|
||
],
|
||
text: aShortNovel,
|
||
data: { justSome: crs( { length: 10 } ) }
|
||
}
|
||
} )
|
||
|
||
const comment = await getComment( { id: commentId } )
|
||
expect( comment.text ).to.equal( aShortNovel )
|
||
} )
|
||
} )
|
||
|
||
|
||
const aShortNovel = `
|
||
In the works of Gaiman, a predominant concept is the concept of
|
||
precapitalist narrativity. Humphrey[1] suggests that we have
|
||
to choose between the structuralist paradigm of context and Derridaist reading.
|
||
But Marx uses the term ‘surrealism’ to denote the meaninglessness of
|
||
materialist society.
|
||
|
||
If one examines the structuralist paradigm of context, one is faced with a
|
||
choice: either accept substructural narrative or conclude that truth is used to
|
||
entrench class divisions, given that Lacan’s analysis of the structuralist
|
||
paradigm of context is valid. Foucault suggests the use of dialectic discourse
|
||
to analyse and challenge class. However, Bataille uses the term ‘the
|
||
constructivist paradigm of expression’ to denote the difference between sexual
|
||
identity and consciousness.
|
||
|
||
The stasis, and some would say the futility, of dialectic discourse
|
||
intrinsic to Gaiman’s Black Orchid is also evident in Sandman.
|
||
But the subject is contextualised into a surrealism that includes narrativity
|
||
as a paradox.
|
||
|
||
The primary theme of the works of Gaiman is not materialism, but
|
||
prematerialism. It could be said that the subject is interpolated into a
|
||
neopatriarchial narrative that includes language as a totality.
|
||
|
||
Dialectic discourse implies that culture is capable of deconstruction.
|
||
Therefore, Lyotard uses the term ‘the structuralist paradigm of context’ to
|
||
denote the failure of structuralist class.
|
||
2. Gaiman and surrealism
|
||
|
||
The characteristic theme of Tilton’s[2] model of the
|
||
structuralist paradigm of context is not deappropriation, as Lacan would have
|
||
it, but subdeappropriation. Baudrillard’s analysis of dialectic discourse holds
|
||
that consensus is created by the collective unconscious, but only if
|
||
consciousness is interchangeable with language. Thus, the subject is
|
||
contextualised into a structuralist paradigm of context that includes
|
||
consciousness as a reality.
|
||
|
||
Derrida uses the term ‘neomodern theory’ to denote the role of the poet as
|
||
writer. But dialectic discourse implies that the State is a legal fiction.
|
||
|
||
Baudrillard uses the term ‘the structuralist paradigm of context’ to denote
|
||
not, in fact, desituationism, but predesituationism. In a sense, the premise of
|
||
Sontagist camp holds that sexuality serves to marginalize the underprivileged.
|
||
|
||
The subject is interpolated into a dialectic discourse that includes art as
|
||
a whole. It could be said that Bataille promotes the use of the structuralist
|
||
paradigm of context to deconstruct sexism.
|
||
` |